modal 1.5.3.dev0__py3-none-any.whl → 1.5.3.dev2__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.
modal/_functions.py CHANGED
@@ -671,6 +671,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
671
671
  # Experimental: Clustered functions
672
672
  cluster_size: int | None = None,
673
673
  rdma: bool | None = None,
674
+ fabric_size: int | None = None,
674
675
  single_use_containers: bool = False,
675
676
  ephemeral_disk: int | None = None,
676
677
  include_source: bool = True,
@@ -1009,6 +1010,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1009
1010
  mount_client_dependencies=mount_client_dependencies,
1010
1011
  # ---
1011
1012
  _experimental_group_size=cluster_size or 0, # Experimental: Clustered functions
1013
+ _experimental_fabric_size=fabric_size or 0, # Experimental: Clustered functions
1012
1014
  _experimental_concurrent_cancellations=True,
1013
1015
  # --- These are deprecated in favor of autoscaler_settings
1014
1016
  warm_pool_size=min_containers or 0,
@@ -1051,6 +1053,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1051
1053
  method_definitions_set=function_definition.method_definitions_set,
1052
1054
  experimental_options=experimental_options or {},
1053
1055
  _experimental_group_size=function_definition._experimental_group_size,
1056
+ _experimental_fabric_size=function_definition._experimental_fabric_size,
1054
1057
  _experimental_buffer_containers=function_definition._experimental_buffer_containers,
1055
1058
  _experimental_custom_scaling=function_definition._experimental_custom_scaling,
1056
1059
  snapshot_debug=function_definition.snapshot_debug,
@@ -74,6 +74,7 @@ class _PartialFunctionParams:
74
74
  target_concurrent_inputs: int | None = None
75
75
  build_timeout: int | None = None
76
76
  rdma: bool | None = None
77
+ fabric_size: int | None = None
77
78
  http_config: api_pb2.HTTPConfig | None = None
78
79
 
79
80
  def update(self, other: "_PartialFunctionParams") -> None:
@@ -777,7 +778,7 @@ def _concurrent(
777
778
 
778
779
  # NOTE: clustered is currently exposed through modal.experimental, not the top-level namespace
779
780
  def _clustered(
780
- size: int, broadcast: bool = True, rdma: bool = False
781
+ size: int, broadcast: bool = True, rdma: bool = False, fabric_size: int | None = None
781
782
  ) -> Callable[
782
783
  [Callable[P, ReturnType] | _PartialFunction[P, ReturnType, ReturnType]],
783
784
  _PartialFunction[P, ReturnType, ReturnType],
@@ -791,6 +792,11 @@ def _clustered(
791
792
  If True, inputs will be sent simultaneously to each container. Otherwise,
792
793
  inputs will be sent only to the rank-0 container, which is responsible for
793
794
  delegating to the workers.
795
+ fabric_size: int | None = None
796
+ Experimental: constrains placement across GPU-memory fabrics. The cluster
797
+ is placed in co-fabric blocks of `fabric_size` containers, so every block
798
+ of `fabric_size` consecutive ranks shares a fabric (the scale-up domain)
799
+ and communicates over cross-node NVLink. Must evenly divide `size`.
794
800
  """
795
801
 
796
802
  assert broadcast, "broadcast=False has not been implemented yet!"
@@ -798,8 +804,14 @@ def _clustered(
798
804
  if size <= 0:
799
805
  raise ValueError("cluster size must be greater than 0")
800
806
 
807
+ if fabric_size is not None:
808
+ if not isinstance(fabric_size, int) or fabric_size <= 0:
809
+ raise ValueError("fabric_size must be a positive integer")
810
+ if size % fabric_size != 0:
811
+ raise ValueError(f"fabric_size must evenly divide the cluster size ({size} % {fabric_size} != 0)")
812
+
801
813
  flags = _PartialFunctionFlags.CLUSTERED
802
- params = _PartialFunctionParams(cluster_size=size, rdma=rdma)
814
+ params = _PartialFunctionParams(cluster_size=size, rdma=rdma, fabric_size=fabric_size)
803
815
 
804
816
  def wrapper(
805
817
  obj: _PartialFunction[P, ReturnType, ReturnType] | Callable[P, ReturnType],
modal/app.py CHANGED
@@ -920,6 +920,7 @@ class _App:
920
920
  i6pn_enabled = i6pn or (f.flags & _PartialFunctionFlags.CLUSTERED)
921
921
  cluster_size = f.params.cluster_size # Experimental: Clustered functions
922
922
  rdma = f.params.rdma
923
+ fabric_size = f.params.fabric_size
923
924
 
924
925
  info = FunctionInfo(f.raw_f, serialized=serialized, name_override=name)
925
926
  raw_f = f.raw_f
@@ -974,6 +975,7 @@ class _App:
974
975
 
975
976
  cluster_size = None # Experimental: Clustered functions
976
977
  rdma = None
978
+ fabric_size = None
977
979
  i6pn_enabled = i6pn
978
980
 
979
981
  if is_generator is None:
@@ -1016,6 +1018,7 @@ class _App:
1016
1018
  i6pn_enabled=i6pn_enabled,
1017
1019
  cluster_size=cluster_size, # Experimental: Clustered functions
1018
1020
  rdma=rdma,
1021
+ fabric_size=fabric_size, # Experimental: Clustered functions
1019
1022
  include_source=include_source if include_source is not None else local_state.include_source_default,
1020
1023
  experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
1021
1024
  restrict_output=_experimental_restrict_output,
@@ -1151,16 +1154,19 @@ class _App:
1151
1154
  if wrapped_cls.flags & _PartialFunctionFlags.CLUSTERED:
1152
1155
  cluster_size = wrapped_cls.params.cluster_size
1153
1156
  rdma = wrapped_cls.params.rdma
1157
+ fabric_size = wrapped_cls.params.fabric_size
1154
1158
 
1155
1159
  else:
1156
1160
  cluster_size = None
1157
1161
  rdma = None
1162
+ fabric_size = None
1158
1163
  else:
1159
1164
  user_cls = wrapped_cls
1160
1165
  max_concurrent_inputs = None
1161
1166
  target_concurrent_inputs = None
1162
1167
  cluster_size = None
1163
1168
  rdma = None
1169
+ fabric_size = None
1164
1170
  if not inspect.isclass(user_cls):
1165
1171
  raise TypeError("The @app.cls decorator must be used on a class.")
1166
1172
 
@@ -1246,6 +1252,7 @@ class _App:
1246
1252
  i6pn_enabled=i6pn_enabled,
1247
1253
  cluster_size=cluster_size,
1248
1254
  rdma=rdma,
1255
+ fabric_size=fabric_size,
1249
1256
  include_source=include_source if include_source is not None else local_state.include_source_default,
1250
1257
  experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
1251
1258
  restrict_output=_experimental_restrict_output,
@@ -1403,6 +1410,7 @@ class _App:
1403
1410
  # Extract the underlying class if wrapped in a _PartialFunction (e.g., from @modal.clustered())
1404
1411
  cluster_size = None
1405
1412
  rdma = None
1413
+ fabric_size = None
1406
1414
  user_cls = wrapped_user_cls
1407
1415
 
1408
1416
  if isinstance(wrapped_user_cls, _PartialFunction):
@@ -1410,6 +1418,7 @@ class _App:
1410
1418
  if wrapped_user_cls.flags & _PartialFunctionFlags.CLUSTERED:
1411
1419
  cluster_size = wrapped_user_cls.params.cluster_size
1412
1420
  rdma = wrapped_user_cls.params.rdma
1421
+ fabric_size = wrapped_user_cls.params.fabric_size
1413
1422
 
1414
1423
  local_state = self._local_state
1415
1424
 
@@ -1449,6 +1458,7 @@ class _App:
1449
1458
  i6pn_enabled=i6pn or (cluster_size is not None),
1450
1459
  cluster_size=cluster_size,
1451
1460
  rdma=rdma,
1461
+ fabric_size=fabric_size,
1452
1462
  include_source=include_source if include_source is not None else local_state.include_source_default,
1453
1463
  experimental_options={k: str(v) for k, v in (experimental_options or {}).items()},
1454
1464
  restrict_output=False,
modal/client.pyi CHANGED
@@ -35,7 +35,7 @@ class _Client:
35
35
  server_url: str,
36
36
  client_type: int,
37
37
  credentials: typing.Optional[tuple[str, str]],
38
- version: str = "1.5.3.dev0",
38
+ version: str = "1.5.3.dev2",
39
39
  ):
40
40
  """mdmd:hidden
41
41
  The Modal client object is not intended to be instantiated directly by users.
@@ -205,7 +205,7 @@ class Client:
205
205
  server_url: str,
206
206
  client_type: int,
207
207
  credentials: typing.Optional[tuple[str, str]],
208
- version: str = "1.5.3.dev0",
208
+ version: str = "1.5.3.dev2",
209
209
  ):
210
210
  """mdmd:hidden
211
211
  The Modal client object is not intended to be instantiated directly by users.
modal/functions.pyi CHANGED
@@ -109,6 +109,7 @@ class Function(
109
109
  i6pn_enabled: bool = False,
110
110
  cluster_size: typing.Optional[int] = None,
111
111
  rdma: typing.Optional[bool] = None,
112
+ fabric_size: typing.Optional[int] = None,
112
113
  single_use_containers: bool = False,
113
114
  ephemeral_disk: typing.Optional[int] = None,
114
115
  include_source: bool = True,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.3.dev0
3
+ Version: 1.5.3.dev2
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -6,7 +6,7 @@ modal/_clustered_functions.pyi,sha256=akbff8aXkp5aJapn72YMXPnzeYa6OFAfuakcy7Jx6w
6
6
  modal/_container_entrypoint.py,sha256=RoO9tnQVsUfzuqi0QzDkYKt-1YR3qlqEUok_jGhXw2s,22822
7
7
  modal/_environments.py,sha256=fpKvtG38NqRiT-4NkBqK1DCZn5LAqPO_vznFIbTCXEE,21016
8
8
  modal/_function_variants.py,sha256=ixkwW9naMMBzRB-92BZd5k3iVWtOoYukveT52hLDIbc,12549
9
- modal/_functions.py,sha256=V8xMSbctRLlxDZzP4jQZHut3RvqwGEIg8bfLaA43iS4,94042
9
+ modal/_functions.py,sha256=aav9lg_LQ-eWwUoN29ai09mLQjtLv0CpnEht2zTlc1o,94280
10
10
  modal/_grpc_client.py,sha256=QL5btzOz6zZzTDcgSwFjI3PBy2gkaycyrlP7Jw-jkbY,6962
11
11
  modal/_image.py,sha256=TW-0QiFa3Lo1w9khZcE8mgSHQgj-PJ0eChb8IkS2eqo,128738
12
12
  modal/_ipython.py,sha256=Bu9fc3hm3xt2O47qLe_d-Mu2jS81LwMsmXNJ1sawBA0,575
@@ -14,7 +14,7 @@ modal/_load_context.py,sha256=wrDqD3KehhFepp0vBhyz_PoLWoRVD0gZHP8dlLHCTiw,4818
14
14
  modal/_location.py,sha256=joiX-0ZeutEUDTrrqLF1GHXCdVLF-rHzstocbMcd_-k,366
15
15
  modal/_logs.py,sha256=JI2z_n2ivKrVRd6IIntyUfadNDetm1Wtu00GyGXBuG4,16938
16
16
  modal/_object.py,sha256=8YXUBrEEvmx45lK5_nshmOX2xNKtPDXGHPzbiJ1YyAE,13503
17
- modal/_partial_function.py,sha256=C432UOhSa3fc2dDS_fJhbwMHIRIR1fcMizDhqCyW1qc,34613
17
+ modal/_partial_function.py,sha256=C1E8F9EWfEzOjQr7RbvMIlqNpsI8Na3I1lefkawiHS8,35377
18
18
  modal/_resolver.py,sha256=LLDM_84aaJfBdgMZ_50CpXJtQzTw5CNRkbLDOiLZEZ0,5309
19
19
  modal/_resources.py,sha256=IS6y7CulfLCjESrpdxFGZr23d5iRCLw6-YQVlE3mKQk,1731
20
20
  modal/_serialization.py,sha256=G07UlvH0SPU62twuwgx8MjD49zFX3te-Mg8bwNYfiEU,26594
@@ -25,13 +25,13 @@ modal/_tunnel.pyi,sha256=WVP0dkoN__mM73ZQ-r3nHKCXvYlRMmEWW8iw2xrSY6M,14698
25
25
  modal/_type_manager.py,sha256=DWjgmjYJuOagw2erin506UUbG2H5UzZCFEekS-7hmfA,9087
26
26
  modal/_watcher.py,sha256=f9kOdHga0TZ6Z6wL7h2O67sz3Oj8pDzjfoAYrNE2hik,3505
27
27
  modal/_workspace.py,sha256=5Cyq8xDcBGMHe5dQlwZggrvBq1knISAi4-g8ZGSZ8yM,14836
28
- modal/app.py,sha256=dw3vJfE_jFnOB4UO585GnY44e8v21SzC8K9rClANjJU,70775
28
+ modal/app.py,sha256=OF0g8hH2vPSpjVKWE7w5_gVil4hUnJbHVfti57Q-FOc,71261
29
29
  modal/app.pyi,sha256=pQN8DZnm5rNEyumZ-sfkcGocjn1MSbA9aor7PHb_qQc,85115
30
30
  modal/billing.py,sha256=E1-P_P8bJFLp-bTaggYd4BaaViImPB5UdtXiY8VeAZQ,390
31
31
  modal/billing.pyi,sha256=4m7VLYNS8IiJ2I3CJx0awvrOM6BILWRVLfdeqtAYr8M,7189
32
32
  modal/call_graph.py,sha256=XGALIDn1bLchWo_goyX23bcPjzCZcfqKUo8INIfDapo,1775
33
33
  modal/client.py,sha256=OgjptM5UuU5LL85_-DFia-ehS8UCgMzVuzfNcxA1qPA,16136
34
- modal/client.pyi,sha256=cX3vwvKcW4097CQW9Ie2g9CjIf8Jxv4xDQGJue86v70,17852
34
+ modal/client.pyi,sha256=TGnoJ3BoRPn-pORdwxHg3D5ZQ7_QHvdoPO8mCvyl6oo,17852
35
35
  modal/cloud_bucket_mount.py,sha256=03gECcV9l2Lnt9k23YrDOTGFW7ajfXMW5O8b1prhtwQ,6254
36
36
  modal/cloud_bucket_mount.pyi,sha256=lMOb44pbNE9kDVEqWfcmvpsnh1sdNNFzuKoVyZZ3d5M,8058
37
37
  modal/cls.py,sha256=A4EW0VDrYtQubZJIzGoxnP1wANWD_2PMpU1Q0n8oUhU,40834
@@ -48,7 +48,7 @@ modal/file_io.py,sha256=GYTa67mrmbTTTBsYQnb51VmwGIo0YY-1qQ3YBhagkmA,21795
48
48
  modal/file_io.pyi,sha256=3181-DS07flFWLf2BKjqCOiWIjieEOolf33wx6fBp5c,15377
49
49
  modal/file_pattern_matcher.py,sha256=6g_awxudMy_ontBvkznybyeNnwGMj297YrmIHCbREI0,8132
50
50
  modal/functions.py,sha256=EZRcHFfnoYbvawUJnAHF5nIvoo9GX2Djy7mUmOCXmXc,258
51
- modal/functions.pyi,sha256=IQwhfJNOT8A2IKczlyEeqZCV-XQUc83MCC6wV_Pg8rI,47593
51
+ modal/functions.pyi,sha256=fVNoXoSs7GJCoQUCpfWAd50iGdmByH0y62mQYM0DpE8,47643
52
52
  modal/image.py,sha256=c0lUSAoU-Ip3g5DGZ0uhdCzoWSQWh1g0i-7Rxo-Sz-I,160
53
53
  modal/image.pyi,sha256=Whbfmda2YLAyzctLEF9uF4FQ5b1canwLojSksRJ4gx8,59716
54
54
  modal/io_streams.py,sha256=3fhhb7SDJ69pVUu9J3PiMLBePD9bpiIxPN05SCzf8FE,29118
@@ -194,7 +194,7 @@ modal/experimental/flash.py,sha256=GAJ1c-EGL8M3_loAkqr5gdUfySMHY3U_oc6RywfZ8-0,3
194
194
  modal/experimental/flash.pyi,sha256=lpTqJFZRMGWscw0xpYZVdBMnAYjj16asXE4_64A6itQ,17774
195
195
  modal/experimental/ipython.py,sha256=kJ6o9wkAY_MiSb7paewI7f1MJAeqdi8TX5pBdhr_mUI,2925
196
196
  modal/skills/modal/SKILL.md,sha256=WzLjYJ1utjc_ZOf6qZTPwB6wxxwRYB5PibdvIf_FLy0,5316
197
- modal-1.5.3.dev0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
197
+ modal-1.5.3.dev2.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
198
198
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
199
199
  modal_docs/gen_cli_docs.py,sha256=9-b8vl2EsLWD9zJqpCK8IUi5h44Bjn3i3rSdedZV4bA,6415
200
200
  modal_docs/gen_cli_docs_main.py,sha256=UPGr7O1DYTIRLwfINe2rbdh89J3cPVTU1OWL-SjxnOQ,198
@@ -206,8 +206,8 @@ modal_docs/mdmd/signatures.py,sha256=OASroKle763Kfumn4Ek4PABJXEGP56JMCTug44QoBx4
206
206
  modal_docs/mdmd/types.py,sha256=MIh5VOnWvFd8T1aZT_vTB2OeLbPB5TASdOaX8jGvpQk,710
207
207
  modal_proto/__init__.py,sha256=tFdSv9eaHBvYcwhgq-XqIlYDNDw57clrvO91Fb-oUV4,130
208
208
  modal_proto/api_grpc.py,sha256=A8qITFFpfpC7vGee-0TChck4CsuKQMhoUeK3kujcszQ,179126
209
- modal_proto/api_pb2.py,sha256=Ey03TKVz7MIwUr3VjWEIAgKjzXsuqH0knGu5DaUskB4,486752
210
- modal_proto/api_pb2.pyi,sha256=6XXF0y_idAh4kPQhx5DnwFP4t3LGWM-BYAY_XxwG8GI,673378
209
+ modal_proto/api_pb2.py,sha256=InjqGNbsfXuy90hsJsNoGFVcmnfDcmnwocuw-RnP_vM,487472
210
+ modal_proto/api_pb2.pyi,sha256=IgGZYgYSojw-Fj8fydD1GeFRsOzZ1FRw7z9qSUD1Mks,675099
211
211
  modal_proto/api_pb2_grpc.py,sha256=nq8fa0wvpAZKFWOM6AEdQFgmM3SqGJN6UWKEVeExor4,384710
212
212
  modal_proto/api_pb2_grpc.pyi,sha256=SxXg2mPSzMYODtRmE_GPZA4u7qYaa4_jVTahZU5h_pk,89930
213
213
  modal_proto/modal_api_grpc.py,sha256=oVMWQ-xpX82WwNl5K0nN9kNS0rfcgTd7hcfqufGu__A,28261
@@ -217,10 +217,10 @@ modal_proto/task_command_router_pb2.py,sha256=LoHxWrzF0EV_gG6SKtAXZbxLUjI-AXzCIG
217
217
  modal_proto/task_command_router_pb2.pyi,sha256=mT-KTIZlLJ5Hd0mXSxyXw84pt_7tV__hd5TpHJ4OHh4,44044
218
218
  modal_proto/task_command_router_pb2_grpc.py,sha256=bX22HJ8qEqKl5YkP00-EPE83rZUdqmk0FDZjtpb21-U,40487
219
219
  modal_proto/task_command_router_pb2_grpc.pyi,sha256=DnkJ4U5hq6wp7XkLyE9xELNqSmNwA_vgawTiS_fVBK0,13241
220
- modal_version/__init__.py,sha256=7INd8JFwae4Gt5M_ZlTv0dwZzIdBY5Fs3y_am3BNXbY,120
220
+ modal_version/__init__.py,sha256=0bxB0DrvR3i4nk6WAxNy7MXXkcQsVIQjgDKtlq8RanA,120
221
221
  modal_version/__main__.py,sha256=nLUbubxKx7GXNzJIkTB7M7_u8080LyLM0IS0rzFkC_c,119
222
- modal-1.5.3.dev0.dist-info/METADATA,sha256=j-S1_F1yd9bmquNzFdPoL945fIkEQC3sbxG6gO8lFbY,2512
223
- modal-1.5.3.dev0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
224
- modal-1.5.3.dev0.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
225
- modal-1.5.3.dev0.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
226
- modal-1.5.3.dev0.dist-info/RECORD,,
222
+ modal-1.5.3.dev2.dist-info/METADATA,sha256=Io_zAgMXDC-K_dNiDl2q0puMwD0ijM2JmXNYBFCzu3o,2512
223
+ modal-1.5.3.dev2.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
224
+ modal-1.5.3.dev2.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
225
+ modal-1.5.3.dev2.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
226
+ modal-1.5.3.dev2.dist-info/RECORD,,