skypilot-nightly 1.0.0.dev20250804__py3-none-any.whl → 1.0.0.dev20250807__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 (151) hide show
  1. sky/__init__.py +2 -2
  2. sky/backends/cloud_vm_ray_backend.py +33 -4
  3. sky/catalog/kubernetes_catalog.py +8 -0
  4. sky/catalog/nebius_catalog.py +0 -1
  5. sky/check.py +11 -1
  6. sky/client/cli/command.py +234 -100
  7. sky/client/sdk.py +30 -9
  8. sky/client/sdk_async.py +815 -0
  9. sky/clouds/kubernetes.py +6 -1
  10. sky/clouds/nebius.py +1 -4
  11. sky/dashboard/out/404.html +1 -1
  12. sky/dashboard/out/_next/static/YAirOGsV1z6B2RJ0VIUmD/_buildManifest.js +1 -0
  13. sky/dashboard/out/_next/static/chunks/1141-a8a8f1adba34c892.js +11 -0
  14. sky/dashboard/out/_next/static/chunks/1871-980a395e92633a5c.js +6 -0
  15. sky/dashboard/out/_next/static/chunks/3785.6003d293cb83eab4.js +1 -0
  16. sky/dashboard/out/_next/static/chunks/{3698-7874720877646365.js → 3850-ff4a9a69d978632b.js} +1 -1
  17. sky/dashboard/out/_next/static/chunks/4725.29550342bd53afd8.js +1 -0
  18. sky/dashboard/out/_next/static/chunks/{4937.d6bf67771e353356.js → 4937.a2baa2df5572a276.js} +1 -1
  19. sky/dashboard/out/_next/static/chunks/6130-2be46d70a38f1e82.js +1 -0
  20. sky/dashboard/out/_next/static/chunks/6601-3e21152fe16da09c.js +1 -0
  21. sky/dashboard/out/_next/static/chunks/{691.6d99cbfba347cebf.js → 691.5eeedf82cc243343.js} +1 -1
  22. sky/dashboard/out/_next/static/chunks/6989-6129c1cfbcf51063.js +1 -0
  23. sky/dashboard/out/_next/static/chunks/6990-0f886f16e0d55ff8.js +1 -0
  24. sky/dashboard/out/_next/static/chunks/8056-019615038d6ce427.js +1 -0
  25. sky/dashboard/out/_next/static/chunks/8252.62b0d23aed618bb2.js +16 -0
  26. sky/dashboard/out/_next/static/chunks/8969-318c3dca725e8e5d.js +1 -0
  27. sky/dashboard/out/_next/static/chunks/{9025.7937c16bc8623516.js → 9025.a1bef12d672bb66d.js} +1 -1
  28. sky/dashboard/out/_next/static/chunks/9159-11421c0f2909236f.js +1 -0
  29. sky/dashboard/out/_next/static/chunks/9360.85b0b1b4054574dd.js +31 -0
  30. sky/dashboard/out/_next/static/chunks/9666.cd4273f2a5c5802c.js +1 -0
  31. sky/dashboard/out/_next/static/chunks/{9847.4c46c5e229c78704.js → 9847.757720f3b40c0aa5.js} +1 -1
  32. sky/dashboard/out/_next/static/chunks/{9984.78ee6d2c6fa4b0e8.js → 9984.c5564679e467d245.js} +1 -1
  33. sky/dashboard/out/_next/static/chunks/pages/{_app-a67ae198457b9886.js → _app-1e6de35d15a8d432.js} +1 -1
  34. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-6fd1d2d8441aa54b.js +11 -0
  35. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-155d477a6c3e04e2.js +1 -0
  36. sky/dashboard/out/_next/static/chunks/pages/clusters-b30460f683e6ba96.js +1 -0
  37. sky/dashboard/out/_next/static/chunks/pages/{config-8620d099cbef8608.js → config-dfb9bf07b13045f4.js} +1 -1
  38. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-13d53fffc03ccb52.js +1 -0
  39. sky/dashboard/out/_next/static/chunks/pages/infra-fc9222e26c8e2f0d.js +1 -0
  40. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-154f55cf8af55be5.js +11 -0
  41. sky/dashboard/out/_next/static/chunks/pages/jobs/pools/[pool]-f5ccf5d39d87aebe.js +21 -0
  42. sky/dashboard/out/_next/static/chunks/pages/jobs-cdc60fb5d371e16a.js +1 -0
  43. sky/dashboard/out/_next/static/chunks/pages/users-7ed36e44e779d5c7.js +1 -0
  44. sky/dashboard/out/_next/static/chunks/pages/volumes-c9695d657f78b5dc.js +1 -0
  45. sky/dashboard/out/_next/static/chunks/pages/workspace/new-3f88a1c7e86a3f86.js +1 -0
  46. sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-f72f73bcef9541dc.js +1 -0
  47. sky/dashboard/out/_next/static/chunks/pages/workspaces-8f67be60165724cc.js +1 -0
  48. sky/dashboard/out/_next/static/chunks/webpack-76efbdad99742559.js +1 -0
  49. sky/dashboard/out/_next/static/css/4614e06482d7309e.css +3 -0
  50. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  51. sky/dashboard/out/clusters/[cluster].html +1 -1
  52. sky/dashboard/out/clusters.html +1 -1
  53. sky/dashboard/out/config.html +1 -1
  54. sky/dashboard/out/index.html +1 -1
  55. sky/dashboard/out/infra/[context].html +1 -1
  56. sky/dashboard/out/infra.html +1 -1
  57. sky/dashboard/out/jobs/[job].html +1 -1
  58. sky/dashboard/out/jobs/pools/[pool].html +1 -0
  59. sky/dashboard/out/jobs.html +1 -1
  60. sky/dashboard/out/users.html +1 -1
  61. sky/dashboard/out/volumes.html +1 -1
  62. sky/dashboard/out/workspace/new.html +1 -1
  63. sky/dashboard/out/workspaces/[name].html +1 -1
  64. sky/dashboard/out/workspaces.html +1 -1
  65. sky/global_user_state.py +14 -2
  66. sky/jobs/__init__.py +2 -0
  67. sky/jobs/client/sdk.py +43 -2
  68. sky/jobs/client/sdk_async.py +135 -0
  69. sky/jobs/server/core.py +48 -1
  70. sky/jobs/server/server.py +52 -3
  71. sky/jobs/state.py +5 -1
  72. sky/jobs/utils.py +3 -1
  73. sky/provision/kubernetes/utils.py +30 -4
  74. sky/provision/nebius/instance.py +1 -0
  75. sky/provision/nebius/utils.py +9 -1
  76. sky/schemas/db/global_user_state/002_add_workspace_to_cluster_history.py +35 -0
  77. sky/schemas/db/spot_jobs/003_pool_hash.py +34 -0
  78. sky/serve/client/impl.py +85 -1
  79. sky/serve/client/sdk.py +16 -47
  80. sky/serve/client/sdk_async.py +130 -0
  81. sky/serve/constants.py +3 -1
  82. sky/serve/controller.py +6 -3
  83. sky/serve/load_balancer.py +3 -1
  84. sky/serve/serve_state.py +93 -5
  85. sky/serve/serve_utils.py +200 -67
  86. sky/serve/server/core.py +13 -197
  87. sky/serve/server/impl.py +261 -23
  88. sky/serve/service.py +15 -3
  89. sky/server/auth/__init__.py +0 -0
  90. sky/server/auth/authn.py +46 -0
  91. sky/server/auth/oauth2_proxy.py +185 -0
  92. sky/server/common.py +119 -21
  93. sky/server/constants.py +1 -1
  94. sky/server/daemons.py +60 -11
  95. sky/server/requests/executor.py +5 -3
  96. sky/server/requests/payloads.py +19 -0
  97. sky/server/rest.py +114 -0
  98. sky/server/server.py +44 -40
  99. sky/setup_files/dependencies.py +2 -0
  100. sky/skylet/constants.py +1 -1
  101. sky/skylet/events.py +5 -1
  102. sky/skylet/skylet.py +3 -1
  103. sky/task.py +61 -21
  104. sky/templates/kubernetes-ray.yml.j2 +9 -0
  105. sky/templates/nebius-ray.yml.j2 +1 -0
  106. sky/templates/sky-serve-controller.yaml.j2 +1 -0
  107. sky/usage/usage_lib.py +8 -6
  108. sky/utils/annotations.py +8 -3
  109. sky/utils/common_utils.py +11 -1
  110. sky/utils/controller_utils.py +7 -0
  111. sky/utils/db/migration_utils.py +2 -2
  112. sky/utils/rich_utils.py +120 -0
  113. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/METADATA +22 -13
  114. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/RECORD +120 -112
  115. sky/client/sdk.pyi +0 -300
  116. sky/dashboard/out/_next/static/KiGGm4fK0CpmN6BT17jkh/_buildManifest.js +0 -1
  117. sky/dashboard/out/_next/static/chunks/1043-928582d4860fef92.js +0 -1
  118. sky/dashboard/out/_next/static/chunks/1141-3f10a5a9f697c630.js +0 -11
  119. sky/dashboard/out/_next/static/chunks/1664-22b00e32c9ff96a4.js +0 -1
  120. sky/dashboard/out/_next/static/chunks/1871-7e17c195296e2ea9.js +0 -6
  121. sky/dashboard/out/_next/static/chunks/2003.f90b06bb1f914295.js +0 -1
  122. sky/dashboard/out/_next/static/chunks/2350.fab69e61bac57b23.js +0 -1
  123. sky/dashboard/out/_next/static/chunks/3785.95524bc443db8260.js +0 -1
  124. sky/dashboard/out/_next/static/chunks/4725.42f21f250f91f65b.js +0 -1
  125. sky/dashboard/out/_next/static/chunks/4869.18e6a4361a380763.js +0 -16
  126. sky/dashboard/out/_next/static/chunks/5230-f3bb2663e442e86c.js +0 -1
  127. sky/dashboard/out/_next/static/chunks/6601-234b1cf963c7280b.js +0 -1
  128. sky/dashboard/out/_next/static/chunks/6989-983d3ae7a874de98.js +0 -1
  129. sky/dashboard/out/_next/static/chunks/6990-08b2a1cae076a943.js +0 -1
  130. sky/dashboard/out/_next/static/chunks/8969-9a8cca241b30db83.js +0 -1
  131. sky/dashboard/out/_next/static/chunks/938-40d15b6261ec8dc1.js +0 -1
  132. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/[job]-fa63e8b1d203f298.js +0 -11
  133. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]-9e7df5fc761c95a7.js +0 -1
  134. sky/dashboard/out/_next/static/chunks/pages/clusters-956ad430075efee8.js +0 -1
  135. sky/dashboard/out/_next/static/chunks/pages/infra/[context]-9cfd875eecb6eaf5.js +0 -1
  136. sky/dashboard/out/_next/static/chunks/pages/infra-0fbdc9072f19fbe2.js +0 -1
  137. sky/dashboard/out/_next/static/chunks/pages/jobs/[job]-6c5af4c86e6ab3d3.js +0 -11
  138. sky/dashboard/out/_next/static/chunks/pages/jobs-6393a9edc7322b54.js +0 -1
  139. sky/dashboard/out/_next/static/chunks/pages/users-34d6bb10c3b3ee3d.js +0 -1
  140. sky/dashboard/out/_next/static/chunks/pages/volumes-225c8dae0634eb7f.js +0 -1
  141. sky/dashboard/out/_next/static/chunks/pages/workspace/new-92f741084a89e27b.js +0 -1
  142. sky/dashboard/out/_next/static/chunks/pages/workspaces/[name]-4d41c9023287f59a.js +0 -1
  143. sky/dashboard/out/_next/static/chunks/pages/workspaces-e4cb7e97d37e93ad.js +0 -1
  144. sky/dashboard/out/_next/static/chunks/webpack-13145516b19858fb.js +0 -1
  145. sky/dashboard/out/_next/static/css/b3227360726f12eb.css +0 -3
  146. /sky/dashboard/out/_next/static/{KiGGm4fK0CpmN6BT17jkh → YAirOGsV1z6B2RJ0VIUmD}/_ssgManifest.js +0 -0
  147. /sky/dashboard/out/_next/static/chunks/{6135-d0e285ac5f3f2485.js → 6135-85426374db04811e.js} +0 -0
  148. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/WHEEL +0 -0
  149. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/entry_points.txt +0 -0
  150. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/licenses/LICENSE +0 -0
  151. {skypilot_nightly-1.0.0.dev20250804.dist-info → skypilot_nightly-1.0.0.dev20250807.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,815 @@
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
+ # TODO(syang): handle the case where the requestID is not
248
+ # provided. https://github.com/skypilot-org/skypilot/issues/6549
249
+ if request_id is None:
250
+ return None
251
+ return await get(request_id)
252
+
253
+ return await stream_response_async(request_id, response,
254
+ output_stream)
255
+ finally:
256
+ response.close()
257
+
258
+
259
+ @usage_lib.entrypoint
260
+ @annotations.client_api
261
+ async def check(
262
+ infra_list: Optional[Tuple[str, ...]],
263
+ verbose: bool,
264
+ workspace: Optional[str] = None,
265
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
266
+ ) -> Dict[str, List[str]]:
267
+ """Async version of check() that checks the credentials to enable clouds."""
268
+ request_id = await context_utils.to_thread(sdk.check, infra_list, verbose,
269
+ workspace)
270
+ if stream_logs is not None:
271
+ return await _stream_and_get(request_id, stream_logs)
272
+ else:
273
+ return await get(request_id)
274
+
275
+
276
+ @usage_lib.entrypoint
277
+ @annotations.client_api
278
+ async def enabled_clouds(
279
+ workspace: Optional[str] = None,
280
+ expand: bool = False,
281
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
282
+ ) -> List[str]:
283
+ """Async version of enabled_clouds() that gets the enabled clouds."""
284
+ request_id = await context_utils.to_thread(sdk.enabled_clouds, workspace,
285
+ expand)
286
+ if stream_logs is not None:
287
+ return await _stream_and_get(request_id, stream_logs)
288
+ else:
289
+ return await get(request_id)
290
+
291
+
292
+ @usage_lib.entrypoint
293
+ @annotations.client_api
294
+ async def list_accelerators(
295
+ gpus_only: bool = True,
296
+ name_filter: Optional[str] = None,
297
+ region_filter: Optional[str] = None,
298
+ quantity_filter: Optional[int] = None,
299
+ clouds: Optional[Union[List[str], str]] = None,
300
+ all_regions: bool = False,
301
+ require_price: bool = True,
302
+ case_sensitive: bool = True,
303
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
304
+ ) -> Dict[str, List[sky.catalog.common.InstanceTypeInfo]]:
305
+ """Async version of list_accelerators() that lists the names of all
306
+ accelerators offered by Sky."""
307
+ request_id = await context_utils.to_thread(sdk.list_accelerators, gpus_only,
308
+ name_filter, region_filter,
309
+ quantity_filter, clouds,
310
+ all_regions, require_price,
311
+ case_sensitive)
312
+ if stream_logs is not None:
313
+ return await _stream_and_get(request_id, stream_logs)
314
+ else:
315
+ return await get(request_id)
316
+
317
+
318
+ @usage_lib.entrypoint
319
+ @annotations.client_api
320
+ async def list_accelerator_counts(
321
+ gpus_only: bool = True,
322
+ name_filter: Optional[str] = None,
323
+ region_filter: Optional[str] = None,
324
+ quantity_filter: Optional[int] = None,
325
+ clouds: Optional[Union[List[str], str]] = None,
326
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
327
+ ) -> Dict[str, List[int]]:
328
+ """Async version of list_accelerator_counts() that lists all accelerators
329
+ offered by Sky and available counts."""
330
+ request_id = await context_utils.to_thread(sdk.list_accelerator_counts,
331
+ gpus_only, name_filter,
332
+ region_filter, quantity_filter,
333
+ clouds)
334
+ if stream_logs is not None:
335
+ return await _stream_and_get(request_id, stream_logs)
336
+ else:
337
+ return await get(request_id)
338
+
339
+
340
+ @usage_lib.entrypoint
341
+ @annotations.client_api
342
+ async def optimize(
343
+ dag: 'sky.Dag',
344
+ minimize: common.OptimizeTarget = common.OptimizeTarget.COST,
345
+ admin_policy_request_options: Optional[
346
+ admin_policy.RequestOptions] = None,
347
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
348
+ ) -> sky.dag.Dag:
349
+ """Async version of optimize() that finds the best execution plan for the
350
+ given DAG."""
351
+ request_id = await context_utils.to_thread(sdk.optimize, dag, minimize,
352
+ admin_policy_request_options)
353
+ if stream_logs is not None:
354
+ return await _stream_and_get(request_id, stream_logs)
355
+ else:
356
+ return await get(request_id)
357
+
358
+
359
+ @usage_lib.entrypoint
360
+ @annotations.client_api
361
+ async def workspaces(
362
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
363
+ ) -> Dict[str, Any]:
364
+ """Async version of workspaces() that gets the workspaces."""
365
+ request_id = await context_utils.to_thread(sdk.workspaces)
366
+ if stream_logs is not None:
367
+ return await _stream_and_get(request_id, stream_logs)
368
+ else:
369
+ return await get(request_id)
370
+
371
+
372
+ @usage_lib.entrypoint
373
+ @annotations.client_api
374
+ async def launch(
375
+ task: Union['sky.Task', 'sky.Dag'],
376
+ cluster_name: Optional[str] = None,
377
+ retry_until_up: bool = False,
378
+ idle_minutes_to_autostop: Optional[int] = None,
379
+ dryrun: bool = False,
380
+ down: bool = False, # pylint: disable=redefined-outer-name
381
+ backend: Optional[backends.Backend] = None,
382
+ optimize_target: common.OptimizeTarget = common.OptimizeTarget.COST,
383
+ no_setup: bool = False,
384
+ clone_disk_from: Optional[str] = None,
385
+ fast: bool = False,
386
+ # Internal only:
387
+ # pylint: disable=invalid-name
388
+ _need_confirmation: bool = False,
389
+ _is_launched_by_jobs_controller: bool = False,
390
+ _is_launched_by_sky_serve_controller: bool = False,
391
+ _disable_controller_check: bool = False,
392
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
393
+ ) -> Tuple[Optional[int], Optional[backends.ResourceHandle]]:
394
+ """Async version of launch() that launches a cluster or task."""
395
+ request_id = await context_utils.to_thread(
396
+ sdk.launch, task, cluster_name, retry_until_up,
397
+ idle_minutes_to_autostop, dryrun, down, backend, optimize_target,
398
+ no_setup, clone_disk_from, fast, _need_confirmation,
399
+ _is_launched_by_jobs_controller, _is_launched_by_sky_serve_controller,
400
+ _disable_controller_check)
401
+ if stream_logs is not None:
402
+ return await _stream_and_get(request_id, stream_logs)
403
+ else:
404
+ return await get(request_id)
405
+
406
+
407
+ @usage_lib.entrypoint
408
+ @annotations.client_api
409
+ async def exec( # pylint: disable=redefined-builtin
410
+ task: Union['sky.Task', 'sky.Dag'],
411
+ cluster_name: Optional[str] = None,
412
+ dryrun: bool = False,
413
+ down: bool = False, # pylint: disable=redefined-outer-name
414
+ backend: Optional[backends.Backend] = None,
415
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
416
+ ) -> Tuple[Optional[int], Optional[backends.ResourceHandle]]:
417
+ """Async version of exec() that executes a task on an existing cluster."""
418
+ request_id = await context_utils.to_thread(sdk.exec, task, cluster_name,
419
+ dryrun, down, backend)
420
+ if stream_logs is not None:
421
+ return await _stream_and_get(request_id, stream_logs)
422
+ else:
423
+ return await get(request_id)
424
+
425
+
426
+ @usage_lib.entrypoint
427
+ @annotations.client_api
428
+ async def tail_logs(cluster_name: str,
429
+ job_id: Optional[int],
430
+ follow: bool,
431
+ tail: int = 0,
432
+ output_stream: Optional['io.TextIOBase'] = None) -> int:
433
+ """Async version of tail_logs() that tails the logs of a job."""
434
+ return await context_utils.to_thread(sdk.tail_logs, cluster_name, job_id,
435
+ follow, tail, output_stream)
436
+
437
+
438
+ @usage_lib.entrypoint
439
+ @annotations.client_api
440
+ async def download_logs(cluster_name: str,
441
+ job_ids: Optional[List[str]]) -> Dict[str, str]:
442
+ """Async version of download_logs() that downloads the logs of jobs."""
443
+ return await context_utils.to_thread(sdk.download_logs, cluster_name,
444
+ job_ids)
445
+
446
+
447
+ @usage_lib.entrypoint
448
+ @annotations.client_api
449
+ async def start(
450
+ cluster_name: str,
451
+ idle_minutes_to_autostop: Optional[int] = None,
452
+ retry_until_up: bool = False,
453
+ down: bool = False, # pylint: disable=redefined-outer-name
454
+ force: bool = False,
455
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
456
+ ) -> backends.CloudVmRayResourceHandle:
457
+ """Async version of start() that restarts a cluster."""
458
+ request_id = await context_utils.to_thread(sdk.start, cluster_name,
459
+ idle_minutes_to_autostop,
460
+ retry_until_up, down, force)
461
+ if stream_logs is not None:
462
+ return await _stream_and_get(request_id, stream_logs)
463
+ else:
464
+ return await get(request_id)
465
+
466
+
467
+ @usage_lib.entrypoint
468
+ @annotations.client_api
469
+ async def down(
470
+ cluster_name: str,
471
+ purge: bool = False,
472
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
473
+ """Async version of down() that tears down a cluster."""
474
+ request_id = await context_utils.to_thread(sdk.down, cluster_name, purge)
475
+ if stream_logs is not None:
476
+ return await _stream_and_get(request_id, stream_logs)
477
+ else:
478
+ return await get(request_id)
479
+
480
+
481
+ @usage_lib.entrypoint
482
+ @annotations.client_api
483
+ async def stop(
484
+ cluster_name: str,
485
+ purge: bool = False,
486
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
487
+ """Async version of stop() that stops a cluster."""
488
+ request_id = await context_utils.to_thread(sdk.stop, cluster_name, purge)
489
+ if stream_logs is not None:
490
+ return await _stream_and_get(request_id, stream_logs)
491
+ else:
492
+ return await get(request_id)
493
+
494
+
495
+ @usage_lib.entrypoint
496
+ @annotations.client_api
497
+ async def autostop(
498
+ cluster_name: str,
499
+ idle_minutes: int,
500
+ down: bool = False, # pylint: disable=redefined-outer-name
501
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
502
+ ) -> None:
503
+ """Async version of autostop() that schedules an autostop/autodown for a
504
+ cluster."""
505
+ request_id = await context_utils.to_thread(sdk.autostop, cluster_name,
506
+ idle_minutes, down)
507
+ if stream_logs is not None:
508
+ return await _stream_and_get(request_id, stream_logs)
509
+ else:
510
+ return await get(request_id)
511
+
512
+
513
+ @usage_lib.entrypoint
514
+ @annotations.client_api
515
+ async def queue(
516
+ cluster_name: str,
517
+ skip_finished: bool = False,
518
+ all_users: bool = False,
519
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
520
+ ) -> List[dict]:
521
+ """Async version of queue() that gets the job queue of a cluster."""
522
+ request_id = await context_utils.to_thread(sdk.queue, cluster_name,
523
+ skip_finished, all_users)
524
+ if stream_logs is not None:
525
+ return await _stream_and_get(request_id, stream_logs)
526
+ else:
527
+ return await get(request_id)
528
+
529
+
530
+ @usage_lib.entrypoint
531
+ @annotations.client_api
532
+ async def job_status(
533
+ cluster_name: str,
534
+ job_ids: Optional[List[int]] = None,
535
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
536
+ ) -> Dict[Optional[int], Optional[job_lib.JobStatus]]:
537
+ """Async version of job_status() that gets the status of jobs on a
538
+ cluster."""
539
+ request_id = await context_utils.to_thread(sdk.job_status, cluster_name,
540
+ job_ids)
541
+ if stream_logs is not None:
542
+ return await _stream_and_get(request_id, stream_logs)
543
+ else:
544
+ return await get(request_id)
545
+
546
+
547
+ @usage_lib.entrypoint
548
+ @annotations.client_api
549
+ async def cancel(
550
+ cluster_name: str,
551
+ all: bool = False, # pylint: disable=redefined-builtin
552
+ all_users: bool = False,
553
+ job_ids: Optional[List[int]] = None,
554
+ # pylint: disable=invalid-name
555
+ _try_cancel_if_cluster_is_init: bool = False,
556
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
557
+ """Async version of cancel() that cancels jobs on a cluster."""
558
+ request_id = await context_utils.to_thread(sdk.cancel, cluster_name, all,
559
+ all_users, job_ids,
560
+ _try_cancel_if_cluster_is_init)
561
+ if stream_logs is not None:
562
+ return await _stream_and_get(request_id, stream_logs)
563
+ else:
564
+ return await get(request_id)
565
+
566
+
567
+ @usage_lib.entrypoint
568
+ @annotations.client_api
569
+ async def status(
570
+ cluster_names: Optional[List[str]] = None,
571
+ refresh: common.StatusRefreshMode = common.StatusRefreshMode.NONE,
572
+ all_users: bool = False,
573
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG,
574
+ ) -> List[Dict[str, Any]]:
575
+ """Async version of status() that gets cluster statuses."""
576
+ request_id = await context_utils.to_thread(sdk.status, cluster_names,
577
+ refresh, all_users)
578
+ if stream_logs is not None:
579
+ return await _stream_and_get(request_id, stream_logs)
580
+ else:
581
+ return await get(request_id)
582
+
583
+
584
+ @usage_lib.entrypoint
585
+ @annotations.client_api
586
+ async def endpoints(
587
+ cluster: str,
588
+ port: Optional[Union[int, str]] = None,
589
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
590
+ ) -> Dict[int, str]:
591
+ """Async version of endpoints() that gets the endpoint for a given cluster
592
+ and port number."""
593
+ request_id = await context_utils.to_thread(sdk.endpoints, cluster, port)
594
+ if stream_logs is not None:
595
+ return await _stream_and_get(request_id, stream_logs)
596
+ else:
597
+ return await get(request_id)
598
+
599
+
600
+ @usage_lib.entrypoint
601
+ @annotations.client_api
602
+ async def cost_report(
603
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
604
+ ) -> List[Dict[str, Any]]:
605
+ """Async version of cost_report() that gets all cluster cost reports."""
606
+ request_id = await context_utils.to_thread(sdk.cost_report)
607
+ if stream_logs is not None:
608
+ return await _stream_and_get(request_id, stream_logs)
609
+ else:
610
+ return await get(request_id)
611
+
612
+
613
+ @usage_lib.entrypoint
614
+ @annotations.client_api
615
+ async def storage_ls(
616
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
617
+ ) -> List[Dict[str, Any]]:
618
+ """Async version of storage_ls() that gets the storages."""
619
+ request_id = await context_utils.to_thread(sdk.storage_ls)
620
+ if stream_logs is not None:
621
+ return await _stream_and_get(request_id, stream_logs)
622
+ else:
623
+ return await get(request_id)
624
+
625
+
626
+ @usage_lib.entrypoint
627
+ @annotations.client_api
628
+ async def storage_delete(
629
+ name: str,
630
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
631
+ """Async version of storage_delete() that deletes a storage."""
632
+ request_id = await context_utils.to_thread(sdk.storage_delete, name)
633
+ if stream_logs is not None:
634
+ return await _stream_and_get(request_id, stream_logs)
635
+ else:
636
+ return await get(request_id)
637
+
638
+
639
+ @usage_lib.entrypoint
640
+ @annotations.client_api
641
+ async def local_up(
642
+ gpus: bool,
643
+ ips: Optional[List[str]],
644
+ ssh_user: Optional[str],
645
+ ssh_key: Optional[str],
646
+ cleanup: bool,
647
+ context_name: Optional[str] = None,
648
+ password: Optional[str] = None,
649
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
650
+ """Async version of local_up() that launches a Kubernetes cluster on
651
+ local machines."""
652
+ request_id = await context_utils.to_thread(sdk.local_up, gpus, ips,
653
+ ssh_user, ssh_key, cleanup,
654
+ context_name, password)
655
+ if stream_logs is not None:
656
+ return await _stream_and_get(request_id, stream_logs)
657
+ else:
658
+ return await get(request_id)
659
+
660
+
661
+ @usage_lib.entrypoint
662
+ @annotations.client_api
663
+ async def local_down(
664
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
665
+ """Async version of local_down() that tears down the Kubernetes cluster
666
+ started by local_up."""
667
+ request_id = await context_utils.to_thread(sdk.local_down)
668
+ if stream_logs is not None:
669
+ return await _stream_and_get(request_id, stream_logs)
670
+ else:
671
+ return await get(request_id)
672
+
673
+
674
+ @usage_lib.entrypoint
675
+ @annotations.client_api
676
+ async def ssh_up(
677
+ infra: Optional[str] = None,
678
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
679
+ """Async version of ssh_up() that deploys the SSH Node Pools defined in
680
+ ~/.sky/ssh_targets.yaml."""
681
+ request_id = await context_utils.to_thread(sdk.ssh_up, infra)
682
+ if stream_logs is not None:
683
+ return await _stream_and_get(request_id, stream_logs)
684
+ else:
685
+ return await get(request_id)
686
+
687
+
688
+ @usage_lib.entrypoint
689
+ @annotations.client_api
690
+ async def ssh_down(
691
+ infra: Optional[str] = None,
692
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG) -> None:
693
+ """Async version of ssh_down() that tears down a Kubernetes cluster on SSH
694
+ targets."""
695
+ request_id = await context_utils.to_thread(sdk.ssh_down, infra)
696
+ if stream_logs is not None:
697
+ return await _stream_and_get(request_id, stream_logs)
698
+ else:
699
+ return await get(request_id)
700
+
701
+
702
+ @usage_lib.entrypoint
703
+ @annotations.client_api
704
+ async def realtime_kubernetes_gpu_availability(
705
+ context: Optional[str] = None,
706
+ name_filter: Optional[str] = None,
707
+ quantity_filter: Optional[int] = None,
708
+ is_ssh: Optional[bool] = None,
709
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
710
+ ) -> List[Tuple[str, List[models.RealtimeGpuAvailability]]]:
711
+ """Async version of realtime_kubernetes_gpu_availability() that gets the
712
+ real-time Kubernetes GPU availability."""
713
+ request_id = await context_utils.to_thread(
714
+ sdk.realtime_kubernetes_gpu_availability, context, name_filter,
715
+ quantity_filter, is_ssh)
716
+ if stream_logs is not None:
717
+ return await _stream_and_get(request_id, stream_logs)
718
+ else:
719
+ return await get(request_id)
720
+
721
+
722
+ @usage_lib.entrypoint
723
+ @annotations.client_api
724
+ async def kubernetes_node_info(
725
+ context: Optional[str] = None,
726
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
727
+ ) -> models.KubernetesNodesInfo:
728
+ """Async version of kubernetes_node_info() that gets the resource
729
+ information for all the nodes in the cluster."""
730
+ request_id = await context_utils.to_thread(sdk.kubernetes_node_info,
731
+ context)
732
+ if stream_logs is not None:
733
+ return await _stream_and_get(request_id, stream_logs)
734
+ else:
735
+ return await get(request_id)
736
+
737
+
738
+ @usage_lib.entrypoint
739
+ @annotations.client_api
740
+ async def status_kubernetes(
741
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
742
+ ) -> Tuple[List[kubernetes_utils.KubernetesSkyPilotClusterInfoPayload],
743
+ List[kubernetes_utils.KubernetesSkyPilotClusterInfoPayload],
744
+ List[Dict[str, Any]], Optional[str]]:
745
+ """Async version of status_kubernetes() that gets all SkyPilot clusters
746
+ and jobs in the Kubernetes cluster."""
747
+ request_id = await context_utils.to_thread(sdk.status_kubernetes)
748
+ if stream_logs is not None:
749
+ return await _stream_and_get(request_id, stream_logs)
750
+ else:
751
+ return await get(request_id)
752
+
753
+
754
+ @usage_lib.entrypoint
755
+ @annotations.client_api
756
+ async def api_cancel(
757
+ request_ids: Optional[Union[str, List[str]]] = None,
758
+ all_users: bool = False,
759
+ silent: bool = False,
760
+ stream_logs: Optional[StreamConfig] = DEFAULT_STREAM_CONFIG
761
+ ) -> List[str]:
762
+ """Async version of api_cancel() that aborts a request or all requests."""
763
+ request_id = await context_utils.to_thread(sdk.api_cancel, request_ids,
764
+ all_users, silent)
765
+ if stream_logs is not None:
766
+ return await _stream_and_get(request_id, stream_logs)
767
+ else:
768
+ return await get(request_id)
769
+
770
+
771
+ @usage_lib.entrypoint
772
+ @annotations.client_api
773
+ async def api_status(request_ids: Optional[List[str]] = None,
774
+ all_status: bool = False) -> List[payloads.RequestPayload]:
775
+ """Async version of api_status() that lists all requests."""
776
+ return await context_utils.to_thread(sdk.api_status, request_ids,
777
+ all_status)
778
+
779
+
780
+ @usage_lib.entrypoint
781
+ @annotations.client_api
782
+ async def dashboard(starting_page: Optional[str] = None) -> None:
783
+ """Async version of dashboard() that starts the dashboard for SkyPilot."""
784
+ return await context_utils.to_thread(sdk.dashboard, starting_page)
785
+
786
+
787
+ @usage_lib.entrypoint
788
+ @annotations.client_api
789
+ async def api_info() -> Dict[str, Any]:
790
+ """Async version of api_info() that gets the server's status, commit and
791
+ version."""
792
+ return await context_utils.to_thread(sdk.api_info)
793
+
794
+
795
+ @usage_lib.entrypoint
796
+ @annotations.client_api
797
+ async def api_stop() -> None:
798
+ """Async version of api_stop() that stops the API server."""
799
+ return await context_utils.to_thread(sdk.api_stop)
800
+
801
+
802
+ @usage_lib.entrypoint
803
+ @annotations.client_api
804
+ async def api_server_logs(follow: bool = True,
805
+ tail: Optional[int] = None) -> None:
806
+ """Async version of api_server_logs() that streams the API server logs."""
807
+ return await context_utils.to_thread(sdk.api_server_logs, follow, tail)
808
+
809
+
810
+ @usage_lib.entrypoint
811
+ @annotations.client_api
812
+ async def api_login(endpoint: Optional[str] = None,
813
+ get_token: bool = False) -> None:
814
+ """Async version of api_login() that logs into a SkyPilot API server."""
815
+ return await context_utils.to_thread(sdk.api_login, endpoint, get_token)