modal 1.1.4.dev30__py3-none-any.whl → 1.1.5.dev0__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/client.pyi CHANGED
@@ -33,7 +33,7 @@ class _Client:
33
33
  server_url: str,
34
34
  client_type: int,
35
35
  credentials: typing.Optional[tuple[str, str]],
36
- version: str = "1.1.4.dev30",
36
+ version: str = "1.1.5.dev0",
37
37
  ):
38
38
  """mdmd:hidden
39
39
  The Modal client object is not intended to be instantiated directly by users.
@@ -164,7 +164,7 @@ class Client:
164
164
  server_url: str,
165
165
  client_type: int,
166
166
  credentials: typing.Optional[tuple[str, str]],
167
- version: str = "1.1.4.dev30",
167
+ version: str = "1.1.5.dev0",
168
168
  ):
169
169
  """mdmd:hidden
170
170
  The Modal client object is not intended to be instantiated directly by users.
@@ -317,14 +317,11 @@ async def notebook_base_image(*, python_version: Optional[str] = None, force_bui
317
317
  # Install uv since it's faster than pip for installing packages.
318
318
  "pip install uv",
319
319
  # https://github.com/astral-sh/uv/issues/11480
320
- "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu126",
320
+ "pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu129",
321
321
  f"uv pip install --system {shlex.join(sorted(environment_packages))}",
322
322
  f"uv pip install --system {shlex.join(sorted(kernelshim_packages))}",
323
323
  ]
324
324
 
325
- # TODO: Also install the CUDA Toolkit, so `nvcc` is available.
326
- # https://github.com/charlesfrye/cuda-modal/blob/7fef8db12402986cf42d9c8cca8c63d1da6d7700/cuda/use_cuda.py#L158-L188
327
-
328
325
  def build_dockerfile(version: ImageBuilderVersion) -> DockerfileSpec:
329
326
  return DockerfileSpec(
330
327
  commands=[
@@ -1,6 +1,8 @@
1
1
  # Copyright Modal Labs 2025
2
2
  import asyncio
3
3
  import math
4
+ import os
5
+ import subprocess
4
6
  import sys
5
7
  import time
6
8
  import traceback
@@ -19,28 +21,87 @@ from ..client import _Client
19
21
  from ..config import logger
20
22
  from ..exception import InvalidError
21
23
 
24
+ MAX_FAILURES = 3
25
+
22
26
 
23
27
  class _FlashManager:
24
- def __init__(self, client: _Client, port: int, health_check_url: Optional[str] = None):
28
+ def __init__(
29
+ self,
30
+ client: _Client,
31
+ port: int,
32
+ process: Optional[subprocess.Popen] = None,
33
+ health_check_url: Optional[str] = None,
34
+ ):
25
35
  self.client = client
26
36
  self.port = port
37
+ # Health check is not currently being used
27
38
  self.health_check_url = health_check_url
39
+ self.process = process
28
40
  self.tunnel_manager = _forward_tunnel(port, client=client)
29
41
  self.stopped = False
42
+ self.num_failures = 0
43
+ self.task_id = os.environ["MODAL_TASK_ID"]
44
+
45
+ async def check_port_connection(self, process: Optional[subprocess.Popen], timeout: int = 10):
46
+ import socket
47
+
48
+ start_time = time.monotonic()
49
+
50
+ while time.monotonic() - start_time < timeout:
51
+ try:
52
+ if process is not None and process.poll() is not None:
53
+ return Exception(f"Process {process.pid} exited with code {process.returncode}")
54
+ with socket.create_connection(("localhost", self.port), timeout=1):
55
+ return
56
+ except (ConnectionRefusedError, OSError):
57
+ await asyncio.sleep(0.1)
58
+
59
+ return Exception(f"Waited too long for port {self.port} to start accepting connections")
30
60
 
31
61
  async def _start(self):
32
62
  self.tunnel = await self.tunnel_manager.__aenter__()
33
-
34
63
  parsed_url = urlparse(self.tunnel.url)
35
64
  host = parsed_url.hostname
36
65
  port = parsed_url.port or 443
37
66
 
38
67
  self.heartbeat_task = asyncio.create_task(self._run_heartbeat(host, port))
68
+ self.drain_task = asyncio.create_task(self._drain_container())
69
+
70
+ async def _drain_container(self):
71
+ """
72
+ Background task that checks if we've encountered too many failures and drains the container if so.
73
+ """
74
+ while True:
75
+ try:
76
+ # Check if the container should be drained (e.g., too many failures)
77
+ if self.num_failures > MAX_FAILURES:
78
+ logger.warning(
79
+ f"[Modal Flash] Draining task {self.task_id} on {self.tunnel.url} due to too many failures."
80
+ )
81
+ await self.stop()
82
+ # handle close upon container exit
83
+
84
+ if self.task_id:
85
+ await self.client.stub.ContainerStop(api_pb2.ContainerStopRequest(task_id=self.task_id))
86
+ return
87
+ except asyncio.CancelledError:
88
+ logger.warning("[Modal Flash] Shutting down...")
89
+ return
90
+ except Exception as e:
91
+ logger.error(f"[Modal Flash] Error draining container: {e}")
92
+ await asyncio.sleep(1)
93
+
94
+ try:
95
+ await asyncio.sleep(1)
96
+ except asyncio.CancelledError:
97
+ logger.warning("[Modal Flash] Shutting down...")
98
+ return
39
99
 
40
100
  async def _run_heartbeat(self, host: str, port: int):
41
101
  first_registration = True
42
102
  while True:
43
103
  try:
104
+ await self.check_port_connection(process=self.process)
44
105
  resp = await self.client.stub.FlashContainerRegister(
45
106
  api_pb2.FlashContainerRegisterRequest(
46
107
  priority=10,
@@ -50,14 +111,25 @@ class _FlashManager:
50
111
  ),
51
112
  timeout=10,
52
113
  )
114
+ self.num_failures = 0
53
115
  if first_registration:
54
- logger.warning(f"[Modal Flash] Listening at {resp.url} over {self.tunnel.url}")
116
+ logger.warning(
117
+ f"[Modal Flash] Listening at {resp.url} over {self.tunnel.url} for task_id {self.task_id}"
118
+ )
55
119
  first_registration = False
56
120
  except asyncio.CancelledError:
57
121
  logger.warning("[Modal Flash] Shutting down...")
58
122
  break
59
123
  except Exception as e:
60
124
  logger.error(f"[Modal Flash] Heartbeat failed: {e}")
125
+ self.num_failures += 1
126
+ logger.error(
127
+ f"[Modal Flash] Deregistering container {self.tunnel.url}, num_failures: {self.num_failures}"
128
+ )
129
+ await retry_transient_errors(
130
+ self.client.stub.FlashContainerDeregister,
131
+ api_pb2.FlashContainerDeregisterRequest(),
132
+ )
61
133
 
62
134
  try:
63
135
  await asyncio.sleep(1)
@@ -94,16 +166,17 @@ FlashManager = synchronize_api(_FlashManager)
94
166
 
95
167
 
96
168
  @synchronizer.create_blocking
97
- async def flash_forward(port: int, health_check_url: Optional[str] = None) -> _FlashManager:
169
+ async def flash_forward(
170
+ port: int, process: Optional[subprocess.Popen] = None, health_check_url: Optional[str] = None
171
+ ) -> _FlashManager:
98
172
  """
99
173
  Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
100
-
101
174
  This is a highly experimental method that can break or be removed at any time without warning.
102
175
  Do not use this method unless explicitly instructed to do so by Modal support.
103
176
  """
104
177
  client = await _Client.from_env()
105
178
 
106
- manager = _FlashManager(client, port, health_check_url)
179
+ manager = _FlashManager(client, port, process=process, health_check_url=health_check_url)
107
180
  await manager._start()
108
181
  return manager
109
182
 
@@ -127,6 +200,8 @@ class _FlashPrometheusAutoscaler:
127
200
  scale_down_stabilization_window_seconds: int,
128
201
  autoscaling_interval_seconds: int,
129
202
  ):
203
+ import aiohttp
204
+
130
205
  if scale_up_stabilization_window_seconds > self._max_window_seconds:
131
206
  raise InvalidError(
132
207
  f"scale_up_stabilization_window_seconds must be less than or equal to {self._max_window_seconds}"
@@ -138,8 +213,6 @@ class _FlashPrometheusAutoscaler:
138
213
  if target_metric_value <= 0:
139
214
  raise InvalidError("target_metric_value must be greater than 0")
140
215
 
141
- import aiohttp
142
-
143
216
  self.client = client
144
217
  self.app_name = app_name
145
218
  self.cls_name = cls_name
@@ -1,14 +1,26 @@
1
1
  import modal.client
2
2
  import modal_proto.api_pb2
3
+ import subprocess
3
4
  import typing
4
5
  import typing_extensions
5
6
 
6
7
  class _FlashManager:
7
- def __init__(self, client: modal.client._Client, port: int, health_check_url: typing.Optional[str] = None):
8
+ def __init__(
9
+ self,
10
+ client: modal.client._Client,
11
+ port: int,
12
+ process: typing.Optional[subprocess.Popen] = None,
13
+ health_check_url: typing.Optional[str] = None,
14
+ ):
8
15
  """Initialize self. See help(type(self)) for accurate signature."""
9
16
  ...
10
17
 
18
+ async def check_port_connection(self, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
11
19
  async def _start(self): ...
20
+ async def _drain_container(self):
21
+ """Background task that checks if we've encountered too many failures and drains the container if so."""
22
+ ...
23
+
12
24
  async def _run_heartbeat(self, host: str, port: int): ...
13
25
  def get_container_url(self): ...
14
26
  async def stop(self): ...
@@ -17,7 +29,19 @@ class _FlashManager:
17
29
  SUPERSELF = typing.TypeVar("SUPERSELF", covariant=True)
18
30
 
19
31
  class FlashManager:
20
- def __init__(self, client: modal.client.Client, port: int, health_check_url: typing.Optional[str] = None): ...
32
+ def __init__(
33
+ self,
34
+ client: modal.client.Client,
35
+ port: int,
36
+ process: typing.Optional[subprocess.Popen] = None,
37
+ health_check_url: typing.Optional[str] = None,
38
+ ): ...
39
+
40
+ class __check_port_connection_spec(typing_extensions.Protocol[SUPERSELF]):
41
+ def __call__(self, /, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
42
+ async def aio(self, /, process: typing.Optional[subprocess.Popen], timeout: int = 10): ...
43
+
44
+ check_port_connection: __check_port_connection_spec[typing_extensions.Self]
21
45
 
22
46
  class ___start_spec(typing_extensions.Protocol[SUPERSELF]):
23
47
  def __call__(self, /): ...
@@ -25,6 +49,17 @@ class FlashManager:
25
49
 
26
50
  _start: ___start_spec[typing_extensions.Self]
27
51
 
52
+ class ___drain_container_spec(typing_extensions.Protocol[SUPERSELF]):
53
+ def __call__(self, /):
54
+ """Background task that checks if we've encountered too many failures and drains the container if so."""
55
+ ...
56
+
57
+ async def aio(self, /):
58
+ """Background task that checks if we've encountered too many failures and drains the container if so."""
59
+ ...
60
+
61
+ _drain_container: ___drain_container_spec[typing_extensions.Self]
62
+
28
63
  class ___run_heartbeat_spec(typing_extensions.Protocol[SUPERSELF]):
29
64
  def __call__(self, /, host: str, port: int): ...
30
65
  async def aio(self, /, host: str, port: int): ...
@@ -46,17 +81,27 @@ class FlashManager:
46
81
  close: __close_spec[typing_extensions.Self]
47
82
 
48
83
  class __flash_forward_spec(typing_extensions.Protocol):
49
- def __call__(self, /, port: int, health_check_url: typing.Optional[str] = None) -> FlashManager:
84
+ def __call__(
85
+ self,
86
+ /,
87
+ port: int,
88
+ process: typing.Optional[subprocess.Popen] = None,
89
+ health_check_url: typing.Optional[str] = None,
90
+ ) -> FlashManager:
50
91
  """Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
51
-
52
92
  This is a highly experimental method that can break or be removed at any time without warning.
53
93
  Do not use this method unless explicitly instructed to do so by Modal support.
54
94
  """
55
95
  ...
56
96
 
57
- async def aio(self, /, port: int, health_check_url: typing.Optional[str] = None) -> FlashManager:
97
+ async def aio(
98
+ self,
99
+ /,
100
+ port: int,
101
+ process: typing.Optional[subprocess.Popen] = None,
102
+ health_check_url: typing.Optional[str] = None,
103
+ ) -> FlashManager:
58
104
  """Forward a port to the Modal Flash service, exposing that port as a stable web endpoint.
59
-
60
105
  This is a highly experimental method that can break or be removed at any time without warning.
61
106
  Do not use this method unless explicitly instructed to do so by Modal support.
62
107
  """
modal/functions.pyi CHANGED
@@ -445,7 +445,7 @@ class Function(
445
445
 
446
446
  _call_generator: ___call_generator_spec[typing_extensions.Self]
447
447
 
448
- class __remote_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
448
+ class __remote_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
449
449
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> ReturnType_INNER:
450
450
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
451
451
  ...
@@ -454,7 +454,7 @@ class Function(
454
454
  """Calls the function remotely, executing it with the given arguments and returning the execution's result."""
455
455
  ...
456
456
 
457
- remote: __remote_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
457
+ remote: __remote_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
458
458
 
459
459
  class __remote_gen_spec(typing_extensions.Protocol[SUPERSELF]):
460
460
  def __call__(self, /, *args, **kwargs) -> typing.Generator[typing.Any, None, None]:
@@ -481,7 +481,7 @@ class Function(
481
481
  """
482
482
  ...
483
483
 
484
- class ___experimental_spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
484
+ class ___experimental_spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
485
485
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
486
486
  """[Experimental] Calls the function with the given arguments, without waiting for the results.
487
487
 
@@ -505,7 +505,7 @@ class Function(
505
505
  ...
506
506
 
507
507
  _experimental_spawn: ___experimental_spawn_spec[
508
- modal._functions.ReturnType, modal._functions.P, typing_extensions.Self
508
+ modal._functions.P, modal._functions.ReturnType, typing_extensions.Self
509
509
  ]
510
510
 
511
511
  class ___spawn_map_inner_spec(typing_extensions.Protocol[P_INNER, SUPERSELF]):
@@ -514,7 +514,7 @@ class Function(
514
514
 
515
515
  _spawn_map_inner: ___spawn_map_inner_spec[modal._functions.P, typing_extensions.Self]
516
516
 
517
- class __spawn_spec(typing_extensions.Protocol[ReturnType_INNER, P_INNER, SUPERSELF]):
517
+ class __spawn_spec(typing_extensions.Protocol[P_INNER, ReturnType_INNER, SUPERSELF]):
518
518
  def __call__(self, /, *args: P_INNER.args, **kwargs: P_INNER.kwargs) -> FunctionCall[ReturnType_INNER]:
519
519
  """Calls the function with the given arguments, without waiting for the results.
520
520
 
@@ -535,7 +535,7 @@ class Function(
535
535
  """
536
536
  ...
537
537
 
538
- spawn: __spawn_spec[modal._functions.ReturnType, modal._functions.P, typing_extensions.Self]
538
+ spawn: __spawn_spec[modal._functions.P, modal._functions.ReturnType, typing_extensions.Self]
539
539
 
540
540
  def get_raw_f(self) -> collections.abc.Callable[..., typing.Any]:
541
541
  """Return the inner Python object wrapped by this Modal Function."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.1.4.dev30
3
+ Version: 1.1.5.dev0
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License: Apache-2.0
@@ -22,7 +22,7 @@ modal/app.py,sha256=F4baVULljFq0CwC_7U-EKNRNx7CYeWBKudjjYUuWc4U,48416
22
22
  modal/app.pyi,sha256=AbXJCBkyt2rI_-M3VbTBYb32at0P6iRZuoC87xY_JrQ,43591
23
23
  modal/call_graph.py,sha256=1g2DGcMIJvRy-xKicuf63IVE98gJSnQsr8R_NVMptNc,2581
24
24
  modal/client.py,sha256=kyAIVB3Ay-XKJizQ_1ufUFB__EagV0MLmHJpyYyJ7J0,18636
25
- modal/client.pyi,sha256=8BYRC3a6lliz1evOa4F_WCEmeVFjvaIsI2klW1RvcJY,15831
25
+ modal/client.pyi,sha256=k-WVSsq2txhtbqe-c29ImNi_Pn0ReiguvKbsPmLFAd0,15829
26
26
  modal/cloud_bucket_mount.py,sha256=YOe9nnvSr4ZbeCn587d7_VhE9IioZYRvF9VYQTQux08,5914
27
27
  modal/cloud_bucket_mount.pyi,sha256=-qSfYAQvIoO_l2wsCCGTG5ZUwQieNKXdAO00yP1-LYU,7394
28
28
  modal/cls.py,sha256=pTEO7pHjlO7taMbIqs4oI9ZZgKDJpVKyGkO5ZT0w6tQ,40934
@@ -39,7 +39,7 @@ modal/file_io.py,sha256=OSKr77TujcXGJW1iikzYiHckLSmv07QBgBHcxxYEkoI,21456
39
39
  modal/file_io.pyi,sha256=xtO6Glf_BFwDE7QiQQo24QqcMf_Vv-iz7WojcGVlLBU,15932
40
40
  modal/file_pattern_matcher.py,sha256=A_Kdkej6q7YQyhM_2-BvpFmPqJ0oHb54B6yf9VqvPVE,8116
41
41
  modal/functions.py,sha256=kcNHvqeGBxPI7Cgd57NIBBghkfbeFJzXO44WW0jSmao,325
42
- modal/functions.pyi,sha256=cDqhpIM5caoCR18_8krpAmPOd4QvEbm1ypYUZ6Ze9Wo,39404
42
+ modal/functions.pyi,sha256=F3Dll-9c3d8c8NuJuEYICUEB-sa67uWU75L111npwbA,39404
43
43
  modal/gpu.py,sha256=Fe5ORvVPDIstSq1xjmM6OoNgLYFWvogP9r5BgmD3hYg,6769
44
44
  modal/image.py,sha256=nXN9k_6gApHFy8-Bk_XT2Zu3jsDsGVrC3QcuiDC4yRY,103543
45
45
  modal/image.pyi,sha256=vKdb5PpYM8wcpq9PQegeVMjrPLzAipuV4q994NZiL84,69325
@@ -149,11 +149,11 @@ modal/cli/programs/launch_instance_ssh.py,sha256=GrwK_Vy8-7B4x5a6AqFaF7lqNVgu75J
149
149
  modal/cli/programs/run_jupyter.py,sha256=44Lpvqk2l3hH-uOkmAOzw60NEsfB5uaRDWDKVshvQhs,2682
150
150
  modal/cli/programs/run_marimo.py,sha256=HyZ2za0NYqg31-mGxFQxUIAJ3Q-jRaMocEwWwI5-cdw,2887
151
151
  modal/cli/programs/vscode.py,sha256=KbTAaIXyQBVCDXxXjmBHmKpgXkUw0q4R4KkJvUjCYgk,3380
152
- modal/experimental/__init__.py,sha256=aWDb1VO9s4D-5Ktw_kYQSqoCp1W8S7lhns-5l7S1l-8,15102
153
- modal/experimental/flash.py,sha256=das7DK9m3PVyCEF9WhxXYDpqyB8x5yp6Z17TX-4b36M,24163
154
- modal/experimental/flash.pyi,sha256=bCH9OgSzwJPo_s5-ObqlNBQUziZ7p5m5_zh0aQTA7sg,12216
152
+ modal/experimental/__init__.py,sha256=fCqzo_f3vcY750vHtd7CtLs5dvdM_C0ZLLGb3zXuK9w,14913
153
+ modal/experimental/flash.py,sha256=aP6ooKk5d1syKn895AzAp4utQMTuZkRjWJFwxZweNbM,27160
154
+ modal/experimental/flash.pyi,sha256=32bvUlolZHthplDJNXokmbjwb0RSOuXGCBpU6qfFPOk,13732
155
155
  modal/experimental/ipython.py,sha256=TrCfmol9LGsRZMeDoeMPx3Hv3BFqQhYnmD_iH0pqdhk,2904
156
- modal-1.1.4.dev30.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
156
+ modal-1.1.5.dev0.dist-info/licenses/LICENSE,sha256=psuoW8kuDP96RQsdhzwOqi6fyWv0ct8CR6Jr7He_P_k,10173
157
157
  modal_docs/__init__.py,sha256=svYKtV8HDwDCN86zbdWqyq5T8sMdGDj0PVlzc2tIxDM,28
158
158
  modal_docs/gen_cli_docs.py,sha256=c1yfBS_x--gL5bs0N4ihMwqwX8l3IBWSkBAKNNIi6bQ,3801
159
159
  modal_docs/gen_reference_docs.py,sha256=d_CQUGQ0rfw28u75I2mov9AlS773z9rG40-yq5o7g2U,6359
@@ -176,10 +176,10 @@ modal_proto/options_pb2.pyi,sha256=l7DBrbLO7q3Ir-XDkWsajm0d0TQqqrfuX54i4BMpdQg,1
176
176
  modal_proto/options_pb2_grpc.py,sha256=1oboBPFxaTEXt9Aw7EAj8gXHDCNMhZD2VXqocC9l_gk,159
177
177
  modal_proto/options_pb2_grpc.pyi,sha256=CImmhxHsYnF09iENPoe8S4J-n93jtgUYD2JPAc0yJSI,247
178
178
  modal_proto/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
179
- modal_version/__init__.py,sha256=Fc2fNsiO6vGtFrDt74uH2WMbtkqcG1uNRwMGmokhpRU,121
179
+ modal_version/__init__.py,sha256=rdFZvW0Nr18GUGPwARaAXlFxwREvq7XLIiKXmERm7Ts,120
180
180
  modal_version/__main__.py,sha256=2FO0yYQQwDTh6udt1h-cBnGd1c4ZyHnHSI4BksxzVac,105
181
- modal-1.1.4.dev30.dist-info/METADATA,sha256=-yL2glfKcCNqhFTQO_7VSHm43rSCAjOTMjqpOmJX-oQ,2460
182
- modal-1.1.4.dev30.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
183
- modal-1.1.4.dev30.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
184
- modal-1.1.4.dev30.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
185
- modal-1.1.4.dev30.dist-info/RECORD,,
181
+ modal-1.1.5.dev0.dist-info/METADATA,sha256=Qtwq_9RN0BFB_Pxc0-DguSJHoA4u5GoBHUqvhtRUMec,2459
182
+ modal-1.1.5.dev0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
183
+ modal-1.1.5.dev0.dist-info/entry_points.txt,sha256=An-wYgeEUnm6xzrAP9_NTSTSciYvvEWsMZILtYrvpAI,46
184
+ modal-1.1.5.dev0.dist-info/top_level.txt,sha256=4BWzoKYREKUZ5iyPzZpjqx4G8uB5TWxXPDwibLcVa7k,43
185
+ modal-1.1.5.dev0.dist-info/RECORD,,
modal_version/__init__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # Copyright Modal Labs 2025
2
2
  """Supplies the current version of the modal client library."""
3
3
 
4
- __version__ = "1.1.4.dev30"
4
+ __version__ = "1.1.5.dev0"