modAL 1.5.4__tar.gz → 1.5.4.dev1__tar.gz

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.
Files changed (238) hide show
  1. {modal-1.5.4 → modal-1.5.4.dev1}/PKG-INFO +1 -1
  2. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_clustered_functions.py +5 -8
  3. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_functions.py +14 -71
  4. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_image.py +11 -71
  5. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_logs.py +7 -9
  6. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_logs_manager.py +108 -340
  7. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_logs_manager.pyi +173 -373
  8. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_output/pty.py +9 -12
  9. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_output/rich.py +2 -12
  10. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/asgi.py +1 -1
  11. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/container_io_manager.py +19 -107
  12. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/container_io_manager.pyi +9 -21
  13. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/execution_context.py +0 -6
  14. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/execution_context.pyi +0 -1
  15. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_server.py +4 -14
  16. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_supports_logs.py +2 -20
  17. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_tunnel.py +2 -2
  18. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/async_utils.py +2 -19
  19. modal-1.5.4.dev1/modal/_utils/auth_token_manager.py +110 -0
  20. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/blob_utils.py +11 -17
  21. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/function_utils.py +4 -32
  22. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/grpc_utils.py +2 -4
  23. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/task_command_router_client.py +13 -59
  24. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/time_utils.py +1 -4
  25. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_watcher.py +2 -4
  26. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_workspace.py +1 -83
  27. {modal-1.5.4 → modal-1.5.4.dev1}/modal/app.py +6 -37
  28. {modal-1.5.4 → modal-1.5.4.dev1}/modal/app.pyi +6 -59
  29. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/_download.py +6 -3
  30. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/app.py +9 -16
  31. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/billing.py +0 -30
  32. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/container.py +99 -66
  33. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/entry_point.py +0 -3
  34. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/image.py +0 -37
  35. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/run.py +8 -36
  36. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/shell.py +7 -6
  37. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/utils.py +6 -8
  38. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/volume.py +17 -14
  39. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/workspace.py +8 -34
  40. {modal-1.5.4 → modal-1.5.4.dev1}/modal/client.py +0 -1
  41. {modal-1.5.4 → modal-1.5.4.dev1}/modal/client.pyi +10 -4
  42. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cls.py +1 -7
  43. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cls.pyi +3 -16
  44. {modal-1.5.4 → modal-1.5.4.dev1}/modal/config.py +0 -1
  45. {modal-1.5.4 → modal-1.5.4.dev1}/modal/dict.py +15 -19
  46. {modal-1.5.4 → modal-1.5.4.dev1}/modal/exception.py +0 -8
  47. {modal-1.5.4 → modal-1.5.4.dev1}/modal/experimental/__init__.py +2 -3
  48. {modal-1.5.4 → modal-1.5.4.dev1}/modal/experimental/flash.py +9 -15
  49. {modal-1.5.4 → modal-1.5.4.dev1}/modal/experimental/flash.pyi +2 -6
  50. {modal-1.5.4 → modal-1.5.4.dev1}/modal/functions.pyi +10 -41
  51. {modal-1.5.4 → modal-1.5.4.dev1}/modal/image.pyi +0 -30
  52. {modal-1.5.4 → modal-1.5.4.dev1}/modal/mount.py +2 -2
  53. {modal-1.5.4 → modal-1.5.4.dev1}/modal/mount.pyi +2 -2
  54. {modal-1.5.4 → modal-1.5.4.dev1}/modal/network_file_system.py +11 -15
  55. {modal-1.5.4 → modal-1.5.4.dev1}/modal/parallel_map.py +12 -14
  56. {modal-1.5.4 → modal-1.5.4.dev1}/modal/parallel_map.pyi +2 -3
  57. {modal-1.5.4 → modal-1.5.4.dev1}/modal/queue.py +15 -19
  58. {modal-1.5.4 → modal-1.5.4.dev1}/modal/runner.py +6 -13
  59. {modal-1.5.4 → modal-1.5.4.dev1}/modal/runner.pyi +3 -9
  60. {modal-1.5.4 → modal-1.5.4.dev1}/modal/sandbox.py +142 -267
  61. {modal-1.5.4 → modal-1.5.4.dev1}/modal/sandbox.pyi +37 -174
  62. {modal-1.5.4 → modal-1.5.4.dev1}/modal/server.pyi +6 -25
  63. {modal-1.5.4 → modal-1.5.4.dev1}/modal/token_flow.py +5 -7
  64. {modal-1.5.4 → modal-1.5.4.dev1}/modal/types.py +3 -49
  65. {modal-1.5.4 → modal-1.5.4.dev1}/modal/volume.py +39 -61
  66. {modal-1.5.4 → modal-1.5.4.dev1}/modal/volume.pyi +15 -21
  67. {modal-1.5.4 → modal-1.5.4.dev1}/modal/workspace.pyi +0 -20
  68. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/PKG-INFO +1 -1
  69. modal-1.5.4.dev1/modal_proto/__init__.py +4 -0
  70. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/api_grpc.py +0 -96
  71. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/api_pb2.py +1282 -1438
  72. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/api_pb2.pyi +26 -381
  73. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/api_pb2_grpc.py +2 -201
  74. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/api_pb2_grpc.pyi +6 -73
  75. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/modal_api_grpc.py +0 -6
  76. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2.py +92 -92
  77. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2.pyi +2 -10
  78. {modal-1.5.4 → modal-1.5.4.dev1}/modal_version/__init__.py +1 -1
  79. {modal-1.5.4 → modal-1.5.4.dev1}/pyproject.toml +35 -10
  80. modal-1.5.4/modal/_utils/auth_token_manager.py +0 -181
  81. modal-1.5.4/modal_proto/__init__.py +0 -11
  82. {modal-1.5.4 → modal-1.5.4.dev1}/LICENSE +0 -0
  83. {modal-1.5.4 → modal-1.5.4.dev1}/README.md +0 -0
  84. {modal-1.5.4 → modal-1.5.4.dev1}/modal/__init__.py +0 -0
  85. {modal-1.5.4 → modal-1.5.4.dev1}/modal/__main__.py +0 -0
  86. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_billing.py +0 -0
  87. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_clustered_functions.pyi +0 -0
  88. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_container_entrypoint.py +0 -0
  89. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_environments.py +0 -0
  90. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_function_variants.py +0 -0
  91. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_grpc_client.py +0 -0
  92. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_ipython.py +0 -0
  93. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_load_context.py +0 -0
  94. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_location.py +0 -0
  95. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_object.py +0 -0
  96. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_output/__init__.py +0 -0
  97. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_output/manager.py +0 -0
  98. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_output/status.py +0 -0
  99. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_partial_function.py +0 -0
  100. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_resolver.py +0 -0
  101. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_resources.py +0 -0
  102. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/__init__.py +0 -0
  103. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/gpu_memory_snapshot.py +0 -0
  104. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/task_lifecycle_manager.py +0 -0
  105. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/task_lifecycle_manager.pyi +0 -0
  106. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/telemetry.py +0 -0
  107. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/user_code_event_loop.py +0 -0
  108. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_runtime/user_code_imports.py +0 -0
  109. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_serialization.py +0 -0
  110. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_traceback.py +0 -0
  111. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_tunnel.pyi +0 -0
  112. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_type_manager.py +0 -0
  113. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/__init__.py +0 -0
  114. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/app_utils.py +0 -0
  115. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/browser_utils.py +0 -0
  116. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/bytes_io_segment_payload.py +0 -0
  117. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/curl_utils.py +0 -0
  118. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/deprecation.py +0 -0
  119. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/display_utils.py +0 -0
  120. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/docker_utils.py +0 -0
  121. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/git_utils.py +0 -0
  122. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/grpc_testing.py +0 -0
  123. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/hash_utils.py +0 -0
  124. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/http_utils.py +0 -0
  125. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/jwt_utils.py +0 -0
  126. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/logger.py +0 -0
  127. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/mount_utils.py +0 -0
  128. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/name_utils.py +0 -0
  129. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/package_utils.py +0 -0
  130. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/pattern_utils.py +0 -0
  131. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/proxy_support.py +0 -0
  132. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/rand_pb_testing.py +0 -0
  133. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/sandbox_fs_utils.py +0 -0
  134. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_utils/shell_utils.py +0 -0
  135. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_vendor/__init__.py +0 -0
  136. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_vendor/a2wsgi_wsgi.py +0 -0
  137. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_vendor/cloudpickle.py +0 -0
  138. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_vendor/tblib.py +0 -0
  139. {modal-1.5.4 → modal-1.5.4.dev1}/modal/_vendor/version.py +0 -0
  140. {modal-1.5.4 → modal-1.5.4.dev1}/modal/billing.py +0 -0
  141. {modal-1.5.4 → modal-1.5.4.dev1}/modal/billing.pyi +0 -0
  142. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/2023.12.312.txt +0 -0
  143. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/2023.12.txt +0 -0
  144. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/2024.04.txt +0 -0
  145. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/2024.10.txt +0 -0
  146. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/2025.06.txt +0 -0
  147. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/PREVIEW.txt +0 -0
  148. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/README.md +0 -0
  149. {modal-1.5.4 → modal-1.5.4.dev1}/modal/builder/base-images.json +0 -0
  150. {modal-1.5.4 → modal-1.5.4.dev1}/modal/call_graph.py +0 -0
  151. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/__init__.py +0 -0
  152. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/_help.py +0 -0
  153. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/_traceback.py +0 -0
  154. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/bootstrap.py +0 -0
  155. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/changelog.py +0 -0
  156. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/cluster.py +0 -0
  157. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/config.py +0 -0
  158. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/curl.py +0 -0
  159. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/dashboard.py +0 -0
  160. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/dict.py +0 -0
  161. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/endpoint.py +0 -0
  162. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/environment.py +0 -0
  163. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/import_refs.py +0 -0
  164. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/launch.py +0 -0
  165. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/logo.py +0 -0
  166. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/network_file_system.py +0 -0
  167. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/profile.py +0 -0
  168. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/programs/__init__.py +0 -0
  169. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/programs/run_jupyter.py +0 -0
  170. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/programs/vscode.py +0 -0
  171. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/queues.py +0 -0
  172. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/secret.py +0 -0
  173. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/selector.py +0 -0
  174. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/skills.py +0 -0
  175. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cli/token.py +0 -0
  176. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cloud_bucket_mount.py +0 -0
  177. {modal-1.5.4 → modal-1.5.4.dev1}/modal/cloud_bucket_mount.pyi +0 -0
  178. {modal-1.5.4 → modal-1.5.4.dev1}/modal/container_process.py +0 -0
  179. {modal-1.5.4 → modal-1.5.4.dev1}/modal/container_process.pyi +0 -0
  180. {modal-1.5.4 → modal-1.5.4.dev1}/modal/dict.pyi +0 -0
  181. {modal-1.5.4 → modal-1.5.4.dev1}/modal/environments.py +0 -0
  182. {modal-1.5.4 → modal-1.5.4.dev1}/modal/environments.pyi +0 -0
  183. {modal-1.5.4 → modal-1.5.4.dev1}/modal/experimental/ipython.py +0 -0
  184. {modal-1.5.4 → modal-1.5.4.dev1}/modal/file_io.py +0 -0
  185. {modal-1.5.4 → modal-1.5.4.dev1}/modal/file_io.pyi +0 -0
  186. {modal-1.5.4 → modal-1.5.4.dev1}/modal/file_pattern_matcher.py +0 -0
  187. {modal-1.5.4 → modal-1.5.4.dev1}/modal/functions.py +0 -0
  188. {modal-1.5.4 → modal-1.5.4.dev1}/modal/image.py +0 -0
  189. {modal-1.5.4 → modal-1.5.4.dev1}/modal/io_streams.py +0 -0
  190. {modal-1.5.4 → modal-1.5.4.dev1}/modal/io_streams.pyi +0 -0
  191. {modal-1.5.4 → modal-1.5.4.dev1}/modal/network_file_system.pyi +0 -0
  192. {modal-1.5.4 → modal-1.5.4.dev1}/modal/object.py +0 -0
  193. {modal-1.5.4 → modal-1.5.4.dev1}/modal/object.pyi +0 -0
  194. {modal-1.5.4 → modal-1.5.4.dev1}/modal/output.py +0 -0
  195. {modal-1.5.4 → modal-1.5.4.dev1}/modal/partial_function.py +0 -0
  196. {modal-1.5.4 → modal-1.5.4.dev1}/modal/partial_function.pyi +0 -0
  197. {modal-1.5.4 → modal-1.5.4.dev1}/modal/proxy.py +0 -0
  198. {modal-1.5.4 → modal-1.5.4.dev1}/modal/proxy.pyi +0 -0
  199. {modal-1.5.4 → modal-1.5.4.dev1}/modal/py.typed +0 -0
  200. {modal-1.5.4 → modal-1.5.4.dev1}/modal/queue.pyi +0 -0
  201. {modal-1.5.4 → modal-1.5.4.dev1}/modal/retries.py +0 -0
  202. {modal-1.5.4 → modal-1.5.4.dev1}/modal/running_app.py +0 -0
  203. {modal-1.5.4 → modal-1.5.4.dev1}/modal/sandbox_fs.py +0 -0
  204. {modal-1.5.4 → modal-1.5.4.dev1}/modal/sandbox_fs.pyi +0 -0
  205. {modal-1.5.4 → modal-1.5.4.dev1}/modal/schedule.py +0 -0
  206. {modal-1.5.4 → modal-1.5.4.dev1}/modal/scheduler_placement.py +0 -0
  207. {modal-1.5.4 → modal-1.5.4.dev1}/modal/secret.py +0 -0
  208. {modal-1.5.4 → modal-1.5.4.dev1}/modal/secret.pyi +0 -0
  209. {modal-1.5.4 → modal-1.5.4.dev1}/modal/server.py +0 -0
  210. {modal-1.5.4 → modal-1.5.4.dev1}/modal/serving.py +0 -0
  211. {modal-1.5.4 → modal-1.5.4.dev1}/modal/serving.pyi +0 -0
  212. {modal-1.5.4 → modal-1.5.4.dev1}/modal/skills/modal/SKILL.md +0 -0
  213. {modal-1.5.4 → modal-1.5.4.dev1}/modal/snapshot.py +0 -0
  214. {modal-1.5.4 → modal-1.5.4.dev1}/modal/snapshot.pyi +0 -0
  215. {modal-1.5.4 → modal-1.5.4.dev1}/modal/stream_type.py +0 -0
  216. {modal-1.5.4 → modal-1.5.4.dev1}/modal/token_flow.pyi +0 -0
  217. {modal-1.5.4 → modal-1.5.4.dev1}/modal/workspace.py +0 -0
  218. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/SOURCES.txt +0 -0
  219. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/dependency_links.txt +0 -0
  220. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/entry_points.txt +0 -0
  221. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/requires.txt +0 -0
  222. {modal-1.5.4 → modal-1.5.4.dev1}/modal.egg-info/top_level.txt +0 -0
  223. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/__init__.py +0 -0
  224. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/gen_cli_docs.py +0 -0
  225. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/gen_cli_docs_main.py +0 -0
  226. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/gen_reference_docs.py +0 -0
  227. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/gen_reference_docs_main.py +0 -0
  228. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/mdmd/__init__.py +0 -0
  229. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/mdmd/mdmd.py +0 -0
  230. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/mdmd/signatures.py +0 -0
  231. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/mdmd/types.py +0 -0
  232. {modal-1.5.4 → modal-1.5.4.dev1}/modal_docs/reference_intro.md +0 -0
  233. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/py.typed +0 -0
  234. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/task_command_router_grpc.py +0 -0
  235. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2_grpc.py +0 -0
  236. {modal-1.5.4 → modal-1.5.4.dev1}/modal_proto/task_command_router_pb2_grpc.pyi +0 -0
  237. {modal-1.5.4 → modal-1.5.4.dev1}/modal_version/__main__.py +0 -0
  238. {modal-1.5.4 → modal-1.5.4.dev1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: modal
3
- Version: 1.5.4
3
+ Version: 1.5.4.dev1
4
4
  Summary: Python client library for Modal
5
5
  Author-email: Modal Labs <support@modal.com>
6
6
  License-Expression: Apache-2.0
@@ -2,7 +2,6 @@
2
2
  import os
3
3
  import socket
4
4
  from dataclasses import dataclass
5
- from typing import cast
6
5
 
7
6
  from modal._utils.async_utils import synchronize_api
8
7
  from modal.client import _Client
@@ -42,11 +41,9 @@ def get_fabric_peers() -> list[int]:
42
41
  async def _initialize_clustered_function(client: _Client, task_id: str, world_size: int):
43
42
  global cluster_info
44
43
 
45
- def get_i6pn() -> str:
44
+ def get_i6pn():
46
45
  """Returns the ipv6 address assigned to this container."""
47
- # An AF_INET6 result always carries a (host, port, flowinfo, scope_id) sockaddr,
48
- # but getaddrinfo is typed for every address family, some of which lead with an int
49
- return cast(str, socket.getaddrinfo("i6pn.modal.local", None, socket.AF_INET6)[0][4][0])
46
+ return socket.getaddrinfo("i6pn.modal.local", None, socket.AF_INET6)[0][4][0]
50
47
 
51
48
  hostname = socket.gethostname()
52
49
  container_ip = get_i6pn()
@@ -80,9 +77,9 @@ async def _initialize_clustered_function(client: _Client, task_id: str, world_si
80
77
  cluster_info = ClusterInfo(
81
78
  rank=resp.cluster_rank,
82
79
  cluster_id=resp.cluster_id,
83
- container_ips=list(resp.container_ips),
84
- container_ipv4_ips=list(resp.container_ipv4_ips),
85
- fabric_ids=list(resp.fabric_ids),
80
+ container_ips=resp.container_ips,
81
+ container_ipv4_ips=resp.container_ipv4_ips,
82
+ fabric_ids=resp.fabric_ids,
86
83
  )
87
84
  else:
88
85
  cluster_info = ClusterInfo(
@@ -45,7 +45,7 @@ from ._utils.async_utils import (
45
45
  synchronizer,
46
46
  warn_if_generator_is_not_consumed,
47
47
  )
48
- from ._utils.blob_utils import MAX_ASYNC_OBJECT_SIZE_BYTES, MAX_OBJECT_SIZE_BYTES
48
+ from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
49
49
  from ._utils.function_utils import (
50
50
  ATTEMPT_TIMEOUT_GRACE_PERIOD,
51
51
  OUTPUTS_TIMEOUT,
@@ -56,9 +56,7 @@ from ._utils.function_utils import (
56
56
  _stream_function_call_data,
57
57
  get_function_type,
58
58
  is_async,
59
- normalize_fractional_target_concurrency,
60
59
  parse_gpu_config,
61
- validate_target_concurrency,
62
60
  )
63
61
  from ._utils.grpc_utils import Retry, RetryWarningMessage
64
62
  from ._utils.mount_utils import validate_network_file_systems, validate_volumes, validate_volumes_by_object_id
@@ -95,7 +93,7 @@ from .proxy import _Proxy
95
93
  from .retries import Retries, RetryManager
96
94
  from .schedule import Schedule
97
95
  from .secret import _Secret
98
- from .types import FunctionAutoscalerSettings, FunctionStats, ServerAutoscalerSettings
96
+ from .types import FunctionStats
99
97
  from .volume import _Volume, _volume_to_mount_proto
100
98
 
101
99
  if TYPE_CHECKING:
@@ -426,7 +424,6 @@ class _InputPlaneInvocation:
426
424
  client: _Client,
427
425
  input_plane_url: str,
428
426
  input_plane_region: str,
429
- function_call_invocation_type: "api_pb2.FunctionCallInvocationType.ValueType",
430
427
  ) -> "_InputPlaneInvocation":
431
428
  stub = await client.get_stub(input_plane_url)
432
429
 
@@ -438,7 +435,6 @@ class _InputPlaneInvocation:
438
435
  kwargs,
439
436
  control_plane_stub,
440
437
  function=function,
441
- function_call_invocation_type=function_call_invocation_type,
442
438
  )
443
439
 
444
440
  request = api_pb2.AttemptStartRequest(
@@ -640,12 +636,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
640
636
  await self.hydrate()
641
637
  if not self._app_id:
642
638
  raise ExecutionError("app_id should have been set during function hydration")
643
- return _LogQueryData(
644
- self.client,
645
- self._app_id,
646
- LogsFilters(function_id=self.object_id),
647
- source_object_id=self.object_id,
648
- )
639
+ return _LogQueryData(self.client, self._app_id, LogsFilters(function_id=self.object_id))
649
640
 
650
641
  @property
651
642
  def logs(self) -> _FunctionLogsManager:
@@ -688,7 +679,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
688
679
  scaleup_window: int | None = None,
689
680
  scaledown_window: int | None = None,
690
681
  max_concurrent_inputs: int | None = None,
691
- target_concurrent_inputs: float | None = None,
682
+ target_concurrent_inputs: int | None = None,
692
683
  batch_max_size: int | None = None,
693
684
  batch_wait_ms: int | None = None,
694
685
  cloud: str | None = None,
@@ -796,17 +787,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
796
787
  scaledown_window=scaledown_window,
797
788
  )
798
789
 
799
- target_concurrent_inputs_int = 0
800
- if target_concurrent_inputs is not None:
801
- if is_server:
802
- validate_target_concurrency(target_concurrent_inputs, "target_inputs", allow_fractional=True)
803
- autoscaler_settings.target_concurrency_float = normalize_fractional_target_concurrency(
804
- target_concurrent_inputs
805
- )
806
- else:
807
- validate_target_concurrency(target_concurrent_inputs, "target_inputs", allow_fractional=False)
808
- target_concurrent_inputs_int = int(target_concurrent_inputs)
809
-
810
790
  # For clustered functions, container settings must be multiples of cluster_size
811
791
  if cluster_size is not None and cluster_size > 1:
812
792
  for field in ["min_containers", "max_containers", "buffer_containers"]:
@@ -1032,7 +1012,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1032
1012
  app_name=app_name,
1033
1013
  is_builder_function=is_builder_function,
1034
1014
  max_concurrent_inputs=max_concurrent_inputs or 0,
1035
- target_concurrent_inputs=target_concurrent_inputs_int,
1015
+ target_concurrent_inputs=target_concurrent_inputs or 0,
1036
1016
  batch_max_size=batch_max_size or 0,
1037
1017
  batch_linger_ms=batch_wait_ms or 0,
1038
1018
  worker_id=config.get("worker_id"),
@@ -1192,25 +1172,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1192
1172
  return obj
1193
1173
 
1194
1174
  async def _update_autoscaler(
1195
- self,
1196
- *,
1197
- min_containers: int | None = None,
1198
- max_containers: int | None = None,
1199
- buffer_containers: int | None = None,
1200
- scaledown_window: int | None = None,
1201
- ) -> FunctionAutoscalerSettings:
1202
- settings = api_pb2.AutoscalerSettings(
1203
- min_containers=min_containers,
1204
- max_containers=max_containers,
1205
- buffer_containers=buffer_containers,
1206
- scaledown_window=scaledown_window,
1207
- )
1208
- request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
1209
- response = await self.client.stub.FunctionUpdateSchedulingParams(request)
1210
-
1211
- return FunctionAutoscalerSettings._from_proto(response.current_settings)
1212
-
1213
- async def _update_autoscaler_server(
1214
1175
  self,
1215
1176
  *,
1216
1177
  min_containers: int | None = None,
@@ -1218,22 +1179,21 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1218
1179
  buffer_containers: int | None = None,
1219
1180
  scaleup_window: int | None = None,
1220
1181
  scaledown_window: int | None = None,
1221
- target_concurrency: float | None = None,
1222
- ) -> ServerAutoscalerSettings:
1182
+ target_concurrency: int | None = None,
1183
+ ) -> None:
1223
1184
  settings = api_pb2.AutoscalerSettings(
1224
1185
  min_containers=min_containers,
1225
1186
  max_containers=max_containers,
1226
1187
  buffer_containers=buffer_containers,
1227
1188
  scaleup_window=scaleup_window,
1228
1189
  scaledown_window=scaledown_window,
1190
+ target_concurrency=target_concurrency,
1229
1191
  )
1230
- if target_concurrency is not None:
1231
- validate_target_concurrency(target_concurrency, "target_concurrency", allow_fractional=True)
1232
- settings.target_concurrency_float = normalize_fractional_target_concurrency(target_concurrency)
1233
1192
  request = api_pb2.FunctionUpdateSchedulingParamsRequest(function_id=self.object_id, settings=settings)
1234
- response = await self.client.stub.FunctionUpdateSchedulingParams(request)
1193
+ await self.client.stub.FunctionUpdateSchedulingParams(request)
1235
1194
 
1236
- return ServerAutoscalerSettings._from_proto(response.current_settings)
1195
+ # One idea would be for FunctionUpdateScheduleParams to return the current (coalesced) settings
1196
+ # and then we could return them here (would need some ad hoc dataclass, which I don't love)
1237
1197
 
1238
1198
  @live_method
1239
1199
  async def update_autoscaler(
@@ -1243,7 +1203,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1243
1203
  max_containers: int | None = None,
1244
1204
  buffer_containers: int | None = None,
1245
1205
  scaledown_window: int | None = None,
1246
- ) -> FunctionAutoscalerSettings:
1206
+ ) -> None:
1247
1207
  """Override the current autoscaler behavior for this Function.
1248
1208
 
1249
1209
  Unspecified parameters will retain their current value, i.e. either the static value
@@ -1258,10 +1218,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1258
1218
  buffer_containers: Extra containers to keep warm beyond current demand.
1259
1219
  scaledown_window: Maximum duration (in seconds) idle containers wait before scaling down.
1260
1220
 
1261
- Returns:
1262
- A `FunctionAutoscalerSettings` dataclass which contains the current autoscaler settings
1263
- of this Function after the call.
1264
-
1265
1221
  Examples:
1266
1222
  ```python notest
1267
1223
  f = modal.Function.from_name("my-app", "function")
@@ -1281,7 +1237,7 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1281
1237
  if self._is_method:
1282
1238
  raise InvalidError("Cannot call .update_autoscaler() on a method. Call it on the class instance instead.")
1283
1239
 
1284
- return await self._update_autoscaler(
1240
+ await self._update_autoscaler(
1285
1241
  min_containers=min_containers,
1286
1242
  max_containers=max_containers,
1287
1243
  buffer_containers=buffer_containers,
@@ -1471,11 +1427,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1471
1427
  self._max_object_size_bytes = (
1472
1428
  metadata.max_object_size_bytes if metadata.HasField("max_object_size_bytes") else MAX_OBJECT_SIZE_BYTES
1473
1429
  )
1474
- self._max_async_object_size_bytes = (
1475
- metadata.max_async_object_size_bytes
1476
- if metadata.HasField("max_async_object_size_bytes")
1477
- else MAX_ASYNC_OBJECT_SIZE_BYTES
1478
- )
1479
1430
  self._experimental_flash_urls = metadata._experimental_flash_urls
1480
1431
  self._app_id = metadata.app_id or None
1481
1432
 
@@ -1500,7 +1451,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1500
1451
  input_plane_url=self._input_plane_url,
1501
1452
  input_plane_region=self._input_plane_region,
1502
1453
  max_object_size_bytes=self._max_object_size_bytes,
1503
- max_async_object_size_bytes=self._max_async_object_size_bytes,
1504
1454
  _experimental_flash_urls=self._experimental_flash_urls,
1505
1455
  supported_input_formats=self._metadata.supported_input_formats if self._metadata else [],
1506
1456
  supported_output_formats=self._metadata.supported_output_formats if self._metadata else [],
@@ -1764,7 +1714,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1764
1714
  client=self.client,
1765
1715
  input_plane_url=self._input_plane_url,
1766
1716
  input_plane_region=self._input_plane_region,
1767
- function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_SYNC,
1768
1717
  )
1769
1718
  else:
1770
1719
  invocation = await _Invocation.create(
@@ -1806,7 +1755,6 @@ class _Function(typing.Generic[P, ReturnType, OriginalReturnType], _Object, type
1806
1755
  client=self.client,
1807
1756
  input_plane_url=self._input_plane_url,
1808
1757
  input_plane_region=self._input_plane_region,
1809
- function_call_invocation_type=api_pb2.FUNCTION_CALL_INVOCATION_TYPE_SYNC_LEGACY,
1810
1758
  )
1811
1759
  else:
1812
1760
  invocation = await _Invocation.create(
@@ -2082,12 +2030,7 @@ class _FunctionCall(typing.Generic[ReturnType], _Object, type_prefix="fc"):
2082
2030
  await self._hydrate_from_id_metadata()
2083
2031
  if not self._app_id:
2084
2032
  raise ExecutionError("app_id should have been set during function call hydration")
2085
- return _LogQueryData(
2086
- self.client,
2087
- self._app_id,
2088
- LogsFilters(function_call_id=self.object_id),
2089
- source_object_id=self.object_id,
2090
- )
2033
+ return _LogQueryData(self.client, self._app_id, LogsFilters(function_call_id=self.object_id))
2091
2034
 
2092
2035
  @property
2093
2036
  def logs(self) -> _FunctionCallLogsManager:
@@ -30,11 +30,9 @@ from modal_proto import api_pb2
30
30
 
31
31
  from ._environments import _get_environment_cached
32
32
  from ._load_context import LoadContext
33
- from ._logs_manager import _ImageLogsManager
34
33
  from ._object import _Object, live_method_gen
35
34
  from ._resolver import Resolver
36
35
  from ._serialization import get_preferred_payload_format, serialize
37
- from ._supports_logs import _ImageLogQueryData
38
36
  from ._utils.async_utils import TaskContext, deprecate_aio_usage, synchronizer
39
37
  from ._utils.blob_utils import MAX_OBJECT_SIZE_BYTES
40
38
  from ._utils.docker_utils import (
@@ -49,10 +47,10 @@ from .cloud_bucket_mount import _CloudBucketMount
49
47
  from .config import config, logger, user_config_path
50
48
  from .exception import (
51
49
  ExecutionError,
52
- ImageBuildError,
53
50
  InternalError,
54
51
  InvalidError,
55
52
  NotFoundError,
53
+ RemoteError,
56
54
  ServiceError,
57
55
  VersionError,
58
56
  )
@@ -479,12 +477,6 @@ def _requires_image_instance(method):
479
477
  return wrapper
480
478
 
481
479
 
482
- @dataclass(frozen=True)
483
- class _FailedBuildAttempt:
484
- image_id: str
485
- client: _Client
486
-
487
-
488
480
  class _Image(_Object, type_prefix="im"):
489
481
  """Base class for container images to run functions in.
490
482
 
@@ -501,9 +493,6 @@ class _Image(_Object, type_prefix="im"):
501
493
  _added_python_source_set: frozenset[str] # used to warn about missing mounts during auto-mount deprecation
502
494
  _metadata: api_pb2.ImageMetadata | None = None # set on hydration, private for now
503
495
  _is_empty: bool
504
- _build_steps: list[api_pb2.ImageBuildStep] | None = None
505
- # A failed image ID is only queryable through the client that performed the build.
506
- _failed_build_attempt: _FailedBuildAttempt | None = None
507
496
 
508
497
  def _initialize_from_empty(self):
509
498
  self.inside_exceptions = []
@@ -535,9 +524,6 @@ class _Image(_Object, type_prefix="im"):
535
524
  if metadata:
536
525
  assert isinstance(metadata, api_pb2.ImageMetadata)
537
526
  self._metadata = metadata
538
- # Successful hydration supersedes diagnostic state retained from a failed build.
539
- self._failed_build_attempt = None
540
- self._build_steps = None
541
527
 
542
528
  def _add_mount_layer_or_copy(self, mount: _Mount, copy: bool = False):
543
529
  if copy:
@@ -766,12 +752,12 @@ class _Image(_Object, type_prefix="im"):
766
752
 
767
753
  if result.status == api_pb2.GenericResult.GENERIC_STATUS_FAILURE:
768
754
  if result.exception:
769
- raise ImageBuildError(
770
- f"Image build for {image_id} failed with the exception:\n{result.exception}", image_id
771
- )
755
+ raise RemoteError(f"Image build for {image_id} failed with the exception:\n{result.exception}")
772
756
  else:
773
- msg = f"Image build for {image_id} failed.\nView the build logs:\n modal image logs {image_id}"
774
- raise ImageBuildError(msg, image_id)
757
+ msg = f"Image build for {image_id} failed. See build logs for more details."
758
+ if not OutputManager.get().is_enabled:
759
+ msg += " (Hint: Use `modal.enable_output()` to see logs from the process building the Image.)"
760
+ raise RemoteError(msg)
775
761
  elif result.status == api_pb2.GenericResult.GENERIC_STATUS_TERMINATED:
776
762
  msg = f"Image build for {image_id} terminated due to external shut-down. Please try again."
777
763
  if result.exception:
@@ -779,16 +765,15 @@ class _Image(_Object, type_prefix="im"):
779
765
  f"Image build for {image_id} terminated due to external shut-down with the exception:\n"
780
766
  f"{result.exception}"
781
767
  )
782
- raise ImageBuildError(msg, image_id)
768
+ raise RemoteError(msg)
783
769
  elif result.status == api_pb2.GenericResult.GENERIC_STATUS_TIMEOUT:
784
- raise ImageBuildError(
785
- f"Image build for {image_id} timed out. Please try again with a larger `timeout` parameter.",
786
- image_id,
770
+ raise RemoteError(
771
+ f"Image build for {image_id} timed out. Please try again with a larger `timeout` parameter."
787
772
  )
788
773
  elif result.status == api_pb2.GenericResult.GENERIC_STATUS_SUCCESS:
789
774
  pass
790
775
  else:
791
- raise ImageBuildError("Unknown status %s!" % result.status, image_id)
776
+ raise RemoteError("Unknown status %s!" % result.status)
792
777
 
793
778
  self._hydrate(image_id, load_context.client, metadata)
794
779
  local_mounts: set[_Mount] = set()
@@ -1079,18 +1064,8 @@ class _Image(_Object, type_prefix="im"):
1079
1064
 
1080
1065
  resolver = Resolver()
1081
1066
  async with TaskContext() as tc:
1082
- self._build_steps = None
1083
- self._failed_build_attempt = None
1084
1067
  load_context = LoadContext(task_context=tc).merged_with(app._root_load_context)
1085
- try:
1086
- await resolver.load(self, load_context)
1087
- except ImageBuildError as e:
1088
- self._failed_build_attempt = _FailedBuildAttempt(
1089
- image_id=e.image_id,
1090
- client=load_context.client,
1091
- )
1092
-
1093
- raise
1068
+ await resolver.load(self, load_context)
1094
1069
  return self
1095
1070
 
1096
1071
  @_requires_image_instance
@@ -3053,38 +3028,3 @@ class _Image(_Object, type_prefix="im"):
3053
3028
  "Images cannot currently be hydrated on demand; you can build an Image by running an App that uses it."
3054
3029
  )
3055
3030
  return self
3056
-
3057
- async def _resolve_image_id_client_for_logs(self) -> tuple[str, _Client]:
3058
- if self._failed_build_attempt is not None:
3059
- return self._failed_build_attempt.image_id, self._failed_build_attempt.client
3060
- if self._object_id is None:
3061
- raise InvalidError("Cannot fetch logs for an image that has not been built yet.")
3062
- if self._is_hydrated:
3063
- return self._object_id, self.client
3064
- # Use self._load_context_overrides to handle case of explicitly passed
3065
- # client in from_id
3066
- client = (await self._load_context_overrides.apply_defaults()).client
3067
- return self._object_id, client
3068
-
3069
- async def _get_log_query_data(self) -> _ImageLogQueryData:
3070
- image_id, client = await self._resolve_image_id_client_for_logs()
3071
-
3072
- if self._build_steps is None:
3073
- request = api_pb2.ImageBuildChainGetRequest(image_id=image_id)
3074
- response = await client.stub.ImageBuildChainGet(request)
3075
- self._build_steps = list(response.build_steps)
3076
- return _ImageLogQueryData(client, self._build_steps or [], image_id)
3077
-
3078
- @property
3079
- def logs(self) -> _ImageLogsManager:
3080
- """Access logs for an `Image`.
3081
-
3082
- Use [`fetch()`](#logsfetch)
3083
- to read logs for individual build layers and [`tail()`](#logstail)
3084
- to read the most recent logs.
3085
-
3086
- See also:
3087
- - [`modal app logs`](https://modal.com/docs/cli/latest/app#modal-app-logs):
3088
- CLI access to logs for an App.
3089
- """
3090
- return _ImageLogsManager(self)
@@ -256,16 +256,13 @@ async def _refine_dense_ranges(
256
256
  )
257
257
  sub_resp = await client.stub.AppCountLogs(sub_req)
258
258
  sub_ranges = _buckets_to_ranges(list(sub_resp.buckets), smaller_secs)
259
- # Clamp the edge sub-ranges to the parent boundaries. Bucket
260
- # alignment can make the first bucket begin before the parent
261
- # (e.g. parent (6,12) with 4s buckets produces (4,8)) or make
262
- # the last bucket end after it (e.g. parent (0,30) with 20s
263
- # buckets produces (20,40)), causing overlaps with siblings.
259
+ # Clamp the last sub-range to the parent's end. When bucket_secs
260
+ # doesn't evenly divide the parent duration, the last bucket
261
+ # boundary overshoots (e.g. parent (0,30) with 20s buckets
262
+ # produces (20,40)), causing overlaps with sibling ranges.
264
263
  if sub_ranges:
265
- first_start, first_end, first_count = sub_ranges[0]
266
- sub_ranges[0] = (max(first_start, start), first_end, first_count)
267
- last_start, last_end, last_count = sub_ranges[-1]
268
- sub_ranges[-1] = (last_start, min(last_end, end), last_count)
264
+ s, _, c = sub_ranges[-1]
265
+ sub_ranges[-1] = (s, min(s + smaller_secs, end), c)
269
266
  return sub_ranges
270
267
 
271
268
  refine_set = {i for i, _ in to_refine}
@@ -413,6 +410,7 @@ async def fetch_logs(
413
410
 
414
411
  if until - since > _MAX_FETCH_RANGE:
415
412
  raise LogsFetchError(f"Time range cannot exceed {_MAX_FETCH_RANGE.days} days.")
413
+
416
414
  # Phase 1: Count logs per bucket
417
415
  bucket_secs = _pick_bucket_secs(since, until)
418
416
  count_req = api_pb2.AppCountLogsRequest(