skypilot-nightly 1.0.0.dev20251019__py3-none-any.whl → 1.0.0.dev20251022__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 (95) hide show
  1. sky/__init__.py +2 -2
  2. sky/adaptors/kubernetes.py +64 -0
  3. sky/backends/backend_utils.py +11 -11
  4. sky/backends/cloud_vm_ray_backend.py +15 -4
  5. sky/client/cli/command.py +39 -10
  6. sky/client/cli/flags.py +4 -2
  7. sky/client/sdk.py +26 -3
  8. sky/dashboard/out/404.html +1 -1
  9. sky/dashboard/out/_next/static/IgACOQPupLbX9z-RYVEDx/_buildManifest.js +1 -0
  10. sky/dashboard/out/_next/static/chunks/1141-ec6f902ffb865853.js +11 -0
  11. sky/dashboard/out/_next/static/chunks/2755.9b1e69c921b5a870.js +26 -0
  12. sky/dashboard/out/_next/static/chunks/3015-d014dc5b9412fade.js +1 -0
  13. sky/dashboard/out/_next/static/chunks/{3294.1fafbf42b3bcebff.js → 3294.998db87cd52a1238.js} +1 -1
  14. sky/dashboard/out/_next/static/chunks/{3785.a19328ba41517b8b.js → 3785.483a3dda2d52f26e.js} +1 -1
  15. sky/dashboard/out/_next/static/chunks/{1121-d0782b9251f0fcd3.js → 4282-d2f3ef2fbf78e347.js} +1 -1
  16. sky/dashboard/out/_next/static/chunks/6856-5c94d394259cdb6e.js +1 -0
  17. sky/dashboard/out/_next/static/chunks/8969-0389e2cb52412db3.js +1 -0
  18. sky/dashboard/out/_next/static/chunks/9360.14326e329484b57e.js +31 -0
  19. sky/dashboard/out/_next/static/chunks/pages/clusters/[cluster]/{[job]-8f058b0346db2aff.js → [job]-602eeead010ec1d6.js} +1 -1
  20. sky/dashboard/out/_next/static/chunks/pages/clusters/{[cluster]-477555ab7c0b13d8.js → [cluster]-18b334dedbd9f6f2.js} +1 -1
  21. sky/dashboard/out/_next/static/chunks/pages/{clusters-2f61f65487f6d8ff.js → clusters-57221ec2e4e01076.js} +1 -1
  22. sky/dashboard/out/_next/static/chunks/pages/infra/{[context]-553b8b5cb65e100b.js → [context]-44ce535a0a0ad4ec.js} +1 -1
  23. sky/dashboard/out/_next/static/chunks/pages/{infra-910a22500c50596f.js → infra-872e6a00165534f4.js} +1 -1
  24. sky/dashboard/out/_next/static/chunks/pages/{jobs-a35a9dc3c5ccd657.js → jobs-0dc34cf9a8710a9f.js} +1 -1
  25. sky/dashboard/out/_next/static/chunks/pages/{users-98d2ed979084162a.js → users-3a543725492fb896.js} +1 -1
  26. sky/dashboard/out/_next/static/chunks/pages/{volumes-835d14ba94808f79.js → volumes-d2af9d22e87cc4ba.js} +1 -1
  27. sky/dashboard/out/_next/static/chunks/pages/workspaces/{[name]-e8688c35c06f0ac5.js → [name]-9ad108cd67d16d96.js} +1 -1
  28. sky/dashboard/out/_next/static/chunks/pages/{workspaces-69c80d677d3c2949.js → workspaces-6fc994fa1ee6c6bf.js} +1 -1
  29. sky/dashboard/out/_next/static/chunks/webpack-919e3c01ab6b2633.js +1 -0
  30. sky/dashboard/out/clusters/[cluster]/[job].html +1 -1
  31. sky/dashboard/out/clusters/[cluster].html +1 -1
  32. sky/dashboard/out/clusters.html +1 -1
  33. sky/dashboard/out/config.html +1 -1
  34. sky/dashboard/out/index.html +1 -1
  35. sky/dashboard/out/infra/[context].html +1 -1
  36. sky/dashboard/out/infra.html +1 -1
  37. sky/dashboard/out/jobs/[job].html +1 -1
  38. sky/dashboard/out/jobs/pools/[pool].html +1 -1
  39. sky/dashboard/out/jobs.html +1 -1
  40. sky/dashboard/out/users.html +1 -1
  41. sky/dashboard/out/volumes.html +1 -1
  42. sky/dashboard/out/workspace/new.html +1 -1
  43. sky/dashboard/out/workspaces/[name].html +1 -1
  44. sky/dashboard/out/workspaces.html +1 -1
  45. sky/data/storage.py +2 -2
  46. sky/global_user_state.py +137 -37
  47. sky/jobs/constants.py +1 -1
  48. sky/jobs/server/core.py +4 -2
  49. sky/jobs/server/server.py +21 -12
  50. sky/jobs/state.py +307 -55
  51. sky/jobs/utils.py +248 -144
  52. sky/provision/kubernetes/network.py +9 -6
  53. sky/provision/provisioner.py +8 -0
  54. sky/schemas/api/responses.py +2 -0
  55. sky/schemas/db/skypilot_config/001_initial_schema.py +30 -0
  56. sky/serve/server/server.py +8 -7
  57. sky/server/common.py +10 -15
  58. sky/server/constants.py +1 -1
  59. sky/server/daemons.py +4 -2
  60. sky/server/requests/executor.py +30 -28
  61. sky/server/requests/payloads.py +5 -1
  62. sky/server/requests/preconditions.py +9 -4
  63. sky/server/requests/requests.py +130 -53
  64. sky/server/requests/serializers/encoders.py +3 -3
  65. sky/server/server.py +91 -58
  66. sky/server/stream_utils.py +127 -38
  67. sky/server/uvicorn.py +18 -17
  68. sky/setup_files/alembic.ini +4 -0
  69. sky/skylet/services.py +5 -5
  70. sky/skypilot_config.py +87 -75
  71. sky/ssh_node_pools/server.py +4 -4
  72. sky/users/permission.py +4 -0
  73. sky/utils/asyncio_utils.py +63 -3
  74. sky/utils/db/db_utils.py +11 -3
  75. sky/utils/db/migration_utils.py +7 -3
  76. sky/volumes/server/server.py +3 -3
  77. sky/workspaces/server.py +6 -6
  78. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/METADATA +37 -37
  79. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/RECORD +87 -86
  80. sky/dashboard/out/_next/static/8e35zdobdd0bK_Nkba03m/_buildManifest.js +0 -1
  81. sky/dashboard/out/_next/static/chunks/1141-3b40c39626f99c89.js +0 -11
  82. sky/dashboard/out/_next/static/chunks/2755.97300e1362fe7c98.js +0 -26
  83. sky/dashboard/out/_next/static/chunks/3015-7e0e8f06bb2f881c.js +0 -1
  84. sky/dashboard/out/_next/static/chunks/6856-5fdc9b851a18acdb.js +0 -1
  85. sky/dashboard/out/_next/static/chunks/8969-66237729cdf9749e.js +0 -1
  86. sky/dashboard/out/_next/static/chunks/9360.71e83b2ddc844ec2.js +0 -31
  87. sky/dashboard/out/_next/static/chunks/webpack-3c431f6c9086e487.js +0 -1
  88. /sky/dashboard/out/_next/static/{8e35zdobdd0bK_Nkba03m → IgACOQPupLbX9z-RYVEDx}/_ssgManifest.js +0 -0
  89. /sky/dashboard/out/_next/static/chunks/{1871-49141c317f3a9020.js → 1871-df9f87fcb7f24292.js} +0 -0
  90. /sky/dashboard/out/_next/static/chunks/pages/jobs/{[job]-e5c9ce6a24fc0de4.js → [job]-8677af16befde039.js} +0 -0
  91. /sky/dashboard/out/_next/static/chunks/pages/jobs/pools/{[pool]-bc979970c247d8f3.js → [pool]-e020fd69dbe76cea.js} +0 -0
  92. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/WHEEL +0 -0
  93. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/entry_points.txt +0 -0
  94. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/licenses/LICENSE +0 -0
  95. {skypilot_nightly-1.0.0.dev20251019.dist-info → skypilot_nightly-1.0.0.dev20251022.dist-info}/top_level.txt +0 -0
@@ -2,17 +2,77 @@
2
2
 
3
3
  import asyncio
4
4
  import functools
5
+ from typing import Set
6
+
7
+ _background_tasks: Set[asyncio.Task] = set()
5
8
 
6
9
 
7
10
  def shield(func):
8
11
  """Shield the decorated async function from cancellation.
9
12
 
10
- Note that filelock.AsyncFileLock is not cancellation safe, thus the
11
- function calls filelock.AsyncFileLock must be shielded.
13
+ If the outter coroutine is cancelled, the inner decorated function
14
+ will be protected from cancellation by asyncio.shield(). And we will
15
+ maintain a reference to the the inner task to avoid it get GCed before
16
+ it is done.
17
+
18
+ For example, filelock.AsyncFileLock is not cancellation safe. The
19
+ following code:
20
+
21
+ async def fn_with_lock():
22
+ async with filelock.AsyncFileLock('lock'):
23
+ await asyncio.sleep(1)
24
+
25
+ is equivalent to:
26
+
27
+ # The lock may leak if the cancellation happens in
28
+ # lock.acquire() or lock.release()
29
+ async def fn_with_lock():
30
+ lock = filelock.AsyncFileLock('lock')
31
+ await lock.acquire()
32
+ try:
33
+ await asyncio.sleep(1)
34
+ finally:
35
+ await lock.release()
36
+
37
+ Shilding the function ensures there is no cancellation will happen in the
38
+ function, thus the lock will be released properly:
39
+
40
+ @shield
41
+ async def fn_with_lock()
42
+
43
+ Note that the resource acquisition and release should usually be protected
44
+ in one @shield block but not separately, e.g.:
45
+
46
+ lock = filelock.AsyncFileLock('lock')
47
+
48
+ @shield
49
+ async def acquire():
50
+ await lock.acquire()
51
+
52
+ @shield
53
+ async def release():
54
+ await lock.release()
55
+
56
+ async def fn_with_lock():
57
+ await acquire()
58
+ try:
59
+ do_something()
60
+ finally:
61
+ await release()
62
+
63
+ The above code is not safe because if `fn_with_lock` is cancelled,
64
+ `acquire()` and `release()` will be executed in the background
65
+ concurrently and causes race conditions.
12
66
  """
13
67
 
14
68
  @functools.wraps(func)
15
69
  async def async_wrapper(*args, **kwargs):
16
- return await asyncio.shield(func(*args, **kwargs))
70
+ task = asyncio.create_task(func(*args, **kwargs))
71
+ try:
72
+ return await asyncio.shield(task)
73
+ except asyncio.CancelledError:
74
+ _background_tasks.add(task)
75
+ task.add_done_callback(lambda _: _background_tasks.discard(task))
76
+ raise
17
77
 
18
78
  return async_wrapper
sky/utils/db/db_utils.py CHANGED
@@ -382,21 +382,28 @@ def get_max_connections():
382
382
 
383
383
  @typing.overload
384
384
  def get_engine(
385
- db_name: str,
385
+ db_name: Optional[str],
386
386
  async_engine: Literal[False] = False) -> sqlalchemy.engine.Engine:
387
387
  ...
388
388
 
389
389
 
390
390
  @typing.overload
391
- def get_engine(db_name: str,
391
+ def get_engine(db_name: Optional[str],
392
392
  async_engine: Literal[True]) -> sqlalchemy_async.AsyncEngine:
393
393
  ...
394
394
 
395
395
 
396
396
  def get_engine(
397
- db_name: str,
397
+ db_name: Optional[str],
398
398
  async_engine: bool = False
399
399
  ) -> Union[sqlalchemy.engine.Engine, sqlalchemy_async.AsyncEngine]:
400
+ """Get the engine for the given database name.
401
+
402
+ Args:
403
+ db_name: The name of the database. ONLY used for SQLite. On Postgres,
404
+ we use a single database, which we get from the connection string.
405
+ async_engine: Whether to return an async engine.
406
+ """
400
407
  conn_string = None
401
408
  if os.environ.get(constants.ENV_VAR_IS_SKYPILOT_SERVER) is not None:
402
409
  conn_string = os.environ.get(constants.ENV_VAR_DB_CONNECTION_URI)
@@ -429,6 +436,7 @@ def get_engine(
429
436
  max_overflow=0))
430
437
  engine = _postgres_engine_cache[conn_string]
431
438
  else:
439
+ assert db_name is not None, 'db_name must be provided for SQLite'
432
440
  db_path = os.path.expanduser(f'~/.sky/{db_name}.db')
433
441
  pathlib.Path(db_path).parents[0].mkdir(parents=True, exist_ok=True)
434
442
  if async_engine:
@@ -19,15 +19,19 @@ DB_INIT_LOCK_TIMEOUT_SECONDS = 10
19
19
 
20
20
  GLOBAL_USER_STATE_DB_NAME = 'state_db'
21
21
  GLOBAL_USER_STATE_VERSION = '010'
22
- GLOBAL_USER_STATE_LOCK_PATH = '~/.sky/locks/.state_db.lock'
22
+ GLOBAL_USER_STATE_LOCK_PATH = f'~/.sky/locks/.{GLOBAL_USER_STATE_DB_NAME}.lock'
23
23
 
24
24
  SPOT_JOBS_DB_NAME = 'spot_jobs_db'
25
25
  SPOT_JOBS_VERSION = '003'
26
- SPOT_JOBS_LOCK_PATH = '~/.sky/locks/.spot_jobs_db.lock'
26
+ SPOT_JOBS_LOCK_PATH = f'~/.sky/locks/.{SPOT_JOBS_DB_NAME}.lock'
27
27
 
28
28
  SERVE_DB_NAME = 'serve_db'
29
29
  SERVE_VERSION = '001'
30
- SERVE_LOCK_PATH = '~/.sky/locks/.serve_db.lock'
30
+ SERVE_LOCK_PATH = f'~/.sky/locks/.{SERVE_DB_NAME}.lock'
31
+
32
+ SKYPILOT_CONFIG_DB_NAME = 'sky_config_db'
33
+ SKYPILOT_CONFIG_VERSION = '001'
34
+ SKYPILOT_CONFIG_LOCK_PATH = f'~/.sky/locks/.{SKYPILOT_CONFIG_DB_NAME}.lock'
31
35
 
32
36
 
33
37
  @contextlib.contextmanager
@@ -25,7 +25,7 @@ async def volume_list(request: fastapi.Request) -> None:
25
25
  'env_vars': auth_user.to_env_vars()
26
26
  } if auth_user else {}
27
27
  request_body = payloads.RequestBody(**auth_user_env_vars_kwargs)
28
- executor.schedule_request(
28
+ await executor.schedule_request_async(
29
29
  request_id=request.state.request_id,
30
30
  request_name='volume_list',
31
31
  request_body=request_body,
@@ -38,7 +38,7 @@ async def volume_list(request: fastapi.Request) -> None:
38
38
  async def volume_delete(request: fastapi.Request,
39
39
  volume_delete_body: payloads.VolumeDeleteBody) -> None:
40
40
  """Deletes a volume."""
41
- executor.schedule_request(
41
+ await executor.schedule_request_async(
42
42
  request_id=request.state.request_id,
43
43
  request_name='volume_delete',
44
44
  request_body=volume_delete_body,
@@ -112,7 +112,7 @@ async def volume_apply(request: fastapi.Request,
112
112
  raise fastapi.HTTPException(
113
113
  status_code=400,
114
114
  detail='Runpod network volume is only supported on Runpod')
115
- executor.schedule_request(
115
+ await executor.schedule_request_async(
116
116
  request_id=request.state.request_id,
117
117
  request_name='volume_apply',
118
118
  request_body=volume_apply_body,
sky/workspaces/server.py CHANGED
@@ -22,7 +22,7 @@ async def get(request: fastapi.Request) -> None:
22
22
  } if auth_user else {}
23
23
  request_body = payloads.RequestBody(**auth_user_env_vars_kwargs)
24
24
 
25
- executor.schedule_request(
25
+ await executor.schedule_request_async(
26
26
  request_id=request.state.request_id,
27
27
  request_name='workspaces.get',
28
28
  request_body=request_body,
@@ -35,7 +35,7 @@ async def get(request: fastapi.Request) -> None:
35
35
  async def update(request: fastapi.Request,
36
36
  update_workspace_body: payloads.UpdateWorkspaceBody) -> None:
37
37
  """Updates a specific workspace configuration."""
38
- executor.schedule_request(
38
+ await executor.schedule_request_async(
39
39
  request_id=request.state.request_id,
40
40
  request_name='workspaces.update',
41
41
  request_body=update_workspace_body,
@@ -48,7 +48,7 @@ async def update(request: fastapi.Request,
48
48
  async def create(request: fastapi.Request,
49
49
  create_workspace_body: payloads.CreateWorkspaceBody) -> None:
50
50
  """Creates a new workspace configuration."""
51
- executor.schedule_request(
51
+ await executor.schedule_request_async(
52
52
  request_id=request.state.request_id,
53
53
  request_name='workspaces.create',
54
54
  request_body=create_workspace_body,
@@ -61,7 +61,7 @@ async def create(request: fastapi.Request,
61
61
  async def delete(request: fastapi.Request,
62
62
  delete_workspace_body: payloads.DeleteWorkspaceBody) -> None:
63
63
  """Deletes a workspace configuration."""
64
- executor.schedule_request(
64
+ await executor.schedule_request_async(
65
65
  request_id=request.state.request_id,
66
66
  request_name='workspaces.delete',
67
67
  request_body=delete_workspace_body,
@@ -78,7 +78,7 @@ async def get_config(request: fastapi.Request) -> None:
78
78
  'env_vars': auth_user.to_env_vars()
79
79
  } if auth_user else {}
80
80
  get_config_body = payloads.GetConfigBody(**auth_user_env_vars_kwargs)
81
- executor.schedule_request(
81
+ await executor.schedule_request_async(
82
82
  request_id=request.state.request_id,
83
83
  request_name='workspaces.get_config',
84
84
  request_body=get_config_body,
@@ -91,7 +91,7 @@ async def get_config(request: fastapi.Request) -> None:
91
91
  async def update_config(request: fastapi.Request,
92
92
  update_config_body: payloads.UpdateConfigBody) -> None:
93
93
  """Updates the entire SkyPilot configuration."""
94
- executor.schedule_request(
94
+ await executor.schedule_request_async(
95
95
  request_id=request.state.request_id,
96
96
  request_name='workspaces.update_config',
97
97
  request_body=update_config_body,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: skypilot-nightly
3
- Version: 1.0.0.dev20251019
3
+ Version: 1.0.0.dev20251022
4
4
  Summary: SkyPilot: Run AI on Any Infra — Unified, Faster, Cheaper.
5
5
  Author: SkyPilot Team
6
6
  License: Apache 2.0
@@ -371,51 +371,51 @@ Requires-Dist: protobuf<7.0.0,>=5.26.1; extra == "shadeform"
371
371
  Requires-Dist: aiosqlite; extra == "shadeform"
372
372
  Requires-Dist: greenlet; extra == "shadeform"
373
373
  Provides-Extra: all
374
+ Requires-Dist: casbin; extra == "all"
375
+ Requires-Dist: google-api-python-client>=2.69.0; extra == "all"
374
376
  Requires-Dist: ibm-vpc; extra == "all"
377
+ Requires-Dist: google-cloud-storage; extra == "all"
378
+ Requires-Dist: aiohttp; extra == "all"
379
+ Requires-Dist: azure-identity>=1.19.0; extra == "all"
375
380
  Requires-Dist: pyvmomi==8.0.1.0.2; extra == "all"
376
- Requires-Dist: cudo-compute>=0.1.10; extra == "all"
381
+ Requires-Dist: msrestazure; extra == "all"
382
+ Requires-Dist: ibm-platform-services>=0.48.0; extra == "all"
383
+ Requires-Dist: oci; extra == "all"
377
384
  Requires-Dist: pyjwt; extra == "all"
378
- Requires-Dist: ibm-cloud-sdk-core; extra == "all"
379
- Requires-Dist: grpcio>=1.63.0; extra == "all"
385
+ Requires-Dist: runpod>=1.6.1; extra == "all"
380
386
  Requires-Dist: docker; extra == "all"
381
- Requires-Dist: pyopenssl<24.3.0,>=23.2.0; extra == "all"
382
- Requires-Dist: sqlalchemy_adapter; extra == "all"
383
- Requires-Dist: azure-mgmt-compute>=33.0.0; extra == "all"
384
- Requires-Dist: awscli>=1.27.10; extra == "all"
387
+ Requires-Dist: azure-common; extra == "all"
385
388
  Requires-Dist: nebius>=0.2.47; extra == "all"
386
- Requires-Dist: websockets; extra == "all"
387
- Requires-Dist: azure-cli>=2.65.0; extra == "all"
388
- Requires-Dist: casbin; extra == "all"
389
- Requires-Dist: aiohttp; extra == "all"
390
- Requires-Dist: anyio; extra == "all"
389
+ Requires-Dist: ibm-cos-sdk; extra == "all"
391
390
  Requires-Dist: protobuf<7.0.0,>=5.26.1; extra == "all"
392
- Requires-Dist: colorama<0.4.5; extra == "all"
393
- Requires-Dist: google-api-python-client>=2.69.0; extra == "all"
394
391
  Requires-Dist: ecsapi>=0.2.0; extra == "all"
395
- Requires-Dist: azure-common; extra == "all"
396
- Requires-Dist: ibm-cos-sdk; extra == "all"
397
- Requires-Dist: azure-storage-blob>=12.23.1; extra == "all"
398
- Requires-Dist: azure-core>=1.31.0; extra == "all"
399
- Requires-Dist: botocore>=1.29.10; extra == "all"
400
- Requires-Dist: tomli; python_version < "3.11" and extra == "all"
401
- Requires-Dist: kubernetes!=32.0.0,>=20.0.0; extra == "all"
402
- Requires-Dist: oci; extra == "all"
403
- Requires-Dist: azure-identity>=1.19.0; extra == "all"
404
- Requires-Dist: ray[default]>=2.6.1; extra == "all"
405
- Requires-Dist: msrestazure; extra == "all"
406
- Requires-Dist: aiosqlite; extra == "all"
407
- Requires-Dist: pydo>=0.3.0; extra == "all"
408
- Requires-Dist: python-dateutil; extra == "all"
409
- Requires-Dist: google-cloud-storage; extra == "all"
410
- Requires-Dist: greenlet; extra == "all"
411
- Requires-Dist: runpod>=1.6.1; extra == "all"
392
+ Requires-Dist: anyio; extra == "all"
393
+ Requires-Dist: ibm-cloud-sdk-core; extra == "all"
394
+ Requires-Dist: sqlalchemy_adapter; extra == "all"
412
395
  Requires-Dist: boto3>=1.26.1; extra == "all"
396
+ Requires-Dist: pyopenssl<24.3.0,>=23.2.0; extra == "all"
397
+ Requires-Dist: aiosqlite; extra == "all"
413
398
  Requires-Dist: vastai-sdk>=0.1.12; extra == "all"
414
- Requires-Dist: azure-core>=1.24.0; extra == "all"
415
- Requires-Dist: ibm-platform-services>=0.48.0; extra == "all"
399
+ Requires-Dist: pydo>=0.3.0; extra == "all"
400
+ Requires-Dist: botocore>=1.29.10; extra == "all"
401
+ Requires-Dist: msgraph-sdk; extra == "all"
402
+ Requires-Dist: awscli>=1.27.10; extra == "all"
416
403
  Requires-Dist: azure-mgmt-network>=27.0.0; extra == "all"
404
+ Requires-Dist: azure-mgmt-compute>=33.0.0; extra == "all"
405
+ Requires-Dist: grpcio>=1.63.0; extra == "all"
406
+ Requires-Dist: kubernetes!=32.0.0,>=20.0.0; extra == "all"
407
+ Requires-Dist: azure-storage-blob>=12.23.1; extra == "all"
408
+ Requires-Dist: colorama<0.4.5; extra == "all"
409
+ Requires-Dist: azure-cli>=2.65.0; extra == "all"
410
+ Requires-Dist: cudo-compute>=0.1.10; extra == "all"
411
+ Requires-Dist: tomli; python_version < "3.11" and extra == "all"
412
+ Requires-Dist: websockets; extra == "all"
413
+ Requires-Dist: azure-core>=1.24.0; extra == "all"
414
+ Requires-Dist: greenlet; extra == "all"
415
+ Requires-Dist: python-dateutil; extra == "all"
416
+ Requires-Dist: ray[default]>=2.6.1; extra == "all"
417
+ Requires-Dist: azure-core>=1.31.0; extra == "all"
417
418
  Requires-Dist: passlib; extra == "all"
418
- Requires-Dist: msgraph-sdk; extra == "all"
419
419
  Provides-Extra: remote
420
420
  Requires-Dist: grpcio>=1.63.0; extra == "remote"
421
421
  Requires-Dist: protobuf<7.0.0,>=5.26.1; extra == "remote"
@@ -479,6 +479,7 @@ Dynamic: summary
479
479
  ----
480
480
 
481
481
  :fire: *News* :fire:
482
+ - [Jul 2025] Run **RL training for LLMs** with SkyRL on your Kubernetes or clouds: [**example**](./llm/skyrl/)
482
483
  - [Oct 2025] Train and serve [Andrej Karpathy's](https://x.com/karpathy/status/1977755427569111362) **nanochat** - the best ChatGPT that $100 can buy: [**example**](./llm/nanochat)
483
484
  - [Oct 2025] Run large-scale **LLM training with TorchTitan** on any AI infra: [**example**](./examples/training/torchtitan)
484
485
  - [Sep 2025] Scaling AI infrastructure at Abridge - **10x faster development** with SkyPilot: [**blog**](https://blog.skypilot.co/abridge/)
@@ -543,8 +544,7 @@ pip install "skypilot-nightly[kubernetes,aws,gcp,azure,oci,nebius,lambda,runpod,
543
544
  </p>
544
545
 
545
546
  Current supported infra: Kubernetes, AWS, GCP, Azure, OCI, Nebius, Lambda Cloud, RunPod, Fluidstack,
546
- Cudo, Digital Ocean, Paperspace, Cloudflare, Samsung, IBM, Vast.ai,
547
- VMware vSphere, Seeweb.
547
+ Cudo, Digital Ocean, Paperspace, Cloudflare, Samsung, IBM, Vast.ai, VMware vSphere, Seeweb, Prime Intellect.
548
548
  <p align="center">
549
549
  <img alt="SkyPilot" src="https://raw.githubusercontent.com/skypilot-org/skypilot/master/docs/source/images/cloud-logos-light.png" width=85%>
550
550
  </p>