letify 2.4.3__tar.gz → 2.4.4__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.

Potentially problematic release.


This version of letify might be problematic. Click here for more details.

Files changed (202) hide show
  1. {letify-2.4.3 → letify-2.4.4}/PKG-INFO +1 -1
  2. {letify-2.4.3 → letify-2.4.4}/letify/__init__.py +1 -1
  3. {letify-2.4.3 → letify-2.4.4}/letify/protocol/worker.py +32 -5
  4. {letify-2.4.3 → letify-2.4.4}/letify-core/Cargo.lock +3 -3
  5. {letify-2.4.3 → letify-2.4.4}/letify-core/Cargo.toml +1 -1
  6. {letify-2.4.3 → letify-2.4.4}/letify-ext/package-lock.json +2 -2
  7. {letify-2.4.3 → letify-2.4.4}/letify-ext/package.json +1 -1
  8. {letify-2.4.3 → letify-2.4.4}/pyproject.toml +1 -1
  9. {letify-2.4.3 → letify-2.4.4}/tests/test_data_streaming.py +47 -0
  10. {letify-2.4.3 → letify-2.4.4}/.github/workflows/ci.yml +0 -0
  11. {letify-2.4.3 → letify-2.4.4}/.github/workflows/docs.yml +0 -0
  12. {letify-2.4.3 → letify-2.4.4}/.github/workflows/publish.yml +0 -0
  13. {letify-2.4.3 → letify-2.4.4}/.gitignore +0 -0
  14. {letify-2.4.3 → letify-2.4.4}/.researchtree +0 -0
  15. {letify-2.4.3 → letify-2.4.4}/LICENSE +0 -0
  16. {letify-2.4.3 → letify-2.4.4}/README.md +0 -0
  17. {letify-2.4.3 → letify-2.4.4}/docs/guide/01-getting-started.md +0 -0
  18. {letify-2.4.3 → letify-2.4.4}/docs/guide/02-providers.md +0 -0
  19. {letify-2.4.3 → letify-2.4.4}/docs/guide/03-execution-modes.md +0 -0
  20. {letify-2.4.3 → letify-2.4.4}/docs/guide/04-environments-and-data.md +0 -0
  21. {letify-2.4.3 → letify-2.4.4}/docs/guide/05-concurrency.md +0 -0
  22. {letify-2.4.3 → letify-2.4.4}/docs/guide/06-cost.md +0 -0
  23. {letify-2.4.3 → letify-2.4.4}/docs/guide/07-troubleshooting.md +0 -0
  24. {letify-2.4.3 → letify-2.4.4}/docs/guide/08-releasing.md +0 -0
  25. {letify-2.4.3 → letify-2.4.4}/docs/guide/README.md +0 -0
  26. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/01-/354/213/234/354/236/221/355/225/230/352/270/260.md" +0 -0
  27. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/02-/355/224/204/353/241/234/353/260/224/354/235/264/353/215/224.md" +0 -0
  28. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/03-/354/213/244/355/226/211-/353/260/251/354/213/235.md" +0 -0
  29. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/04-/355/231/230/352/262/275/352/263/274-/353/215/260/354/235/264/355/204/260.md" +0 -0
  30. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/05-/353/217/231/354/213/234-/354/213/244/355/226/211.md" +0 -0
  31. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/06-/353/271/204/354/232/251.md" +0 -0
  32. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/07-/353/254/270/354/240/234-/355/225/264/352/262/260.md" +0 -0
  33. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/08-/353/246/264/353/246/254/354/212/244.md" +0 -0
  34. {letify-2.4.3 → letify-2.4.4}/docs/guide/ko/README.md +0 -0
  35. {letify-2.4.3 → letify-2.4.4}/docs/locales/README_ko.md +0 -0
  36. {letify-2.4.3 → letify-2.4.4}/docs/mkdocs.yml +0 -0
  37. {letify-2.4.3 → letify-2.4.4}/examples/README.md +0 -0
  38. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/.letify.example +0 -0
  39. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/00_smoke.py +0 -0
  40. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/01_sweep.py +0 -0
  41. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/02_resume.py +0 -0
  42. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/03_watch.py +0 -0
  43. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/README.md +0 -0
  44. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/README_ko.md +0 -0
  45. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/common.py +0 -0
  46. {letify-2.4.3 → letify-2.4.4}/examples/nvfp4-lora/recipe.py +0 -0
  47. {letify-2.4.3 → letify-2.4.4}/letify/cli.py +0 -0
  48. {letify-2.4.3 → letify-2.4.4}/letify/config/__init__.py +0 -0
  49. {letify-2.4.3 → letify-2.4.4}/letify/config/inventory.py +0 -0
  50. {letify-2.4.3 → letify-2.4.4}/letify/config/login.py +0 -0
  51. {letify-2.4.3 → letify-2.4.4}/letify/config/schema.py +0 -0
  52. {letify-2.4.3 → letify-2.4.4}/letify/config/secrets.py +0 -0
  53. {letify-2.4.3 → letify-2.4.4}/letify/config/writer.py +0 -0
  54. {letify-2.4.3 → letify-2.4.4}/letify/declare/__init__.py +0 -0
  55. {letify-2.4.3 → letify-2.4.4}/letify/declare/cache.py +0 -0
  56. {letify-2.4.3 → letify-2.4.4}/letify/declare/env.py +0 -0
  57. {letify-2.4.3 → letify-2.4.4}/letify/declare/function.py +0 -0
  58. {letify-2.4.3 → letify-2.4.4}/letify/declare/instance.py +0 -0
  59. {letify-2.4.3 → letify-2.4.4}/letify/declare/replicas.py +0 -0
  60. {letify-2.4.3 → letify-2.4.4}/letify/errors.py +0 -0
  61. {letify-2.4.3 → letify-2.4.4}/letify/install.py +0 -0
  62. {letify-2.4.3 → letify-2.4.4}/letify/launcher.py +0 -0
  63. {letify-2.4.3 → letify-2.4.4}/letify/paths.py +0 -0
  64. {letify-2.4.3 → letify-2.4.4}/letify/protocol/__init__.py +0 -0
  65. {letify-2.4.3 → letify-2.4.4}/letify/protocol/codec.py +0 -0
  66. {letify-2.4.3 → letify-2.4.4}/letify/protocol/driver.py +0 -0
  67. {letify-2.4.3 → letify-2.4.4}/letify/protocol/handle.py +0 -0
  68. {letify-2.4.3 → letify-2.4.4}/letify/protocol/wire.py +0 -0
  69. {letify-2.4.3 → letify-2.4.4}/letify/providers/__init__.py +0 -0
  70. {letify-2.4.3 → letify-2.4.4}/letify/providers/base.py +0 -0
  71. {letify-2.4.3 → letify-2.4.4}/letify/providers/colab.py +0 -0
  72. {letify-2.4.3 → letify-2.4.4}/letify/providers/colab_files.py +0 -0
  73. {letify-2.4.3 → letify-2.4.4}/letify/providers/colab_keepalive.py +0 -0
  74. {letify-2.4.3 → letify-2.4.4}/letify/providers/elice.py +0 -0
  75. {letify-2.4.3 → letify-2.4.4}/letify/providers/kaggle.py +0 -0
  76. {letify-2.4.3 → letify-2.4.4}/letify/providers/kaggle_adapter.py +0 -0
  77. {letify-2.4.3 → letify-2.4.4}/letify/providers/local.py +0 -0
  78. {letify-2.4.3 → letify-2.4.4}/letify/providers/modal.py +0 -0
  79. {letify-2.4.3 → letify-2.4.4}/letify/providers/modal_adapter.py +0 -0
  80. {letify-2.4.3 → letify-2.4.4}/letify/providers/naming.py +0 -0
  81. {letify-2.4.3 → letify-2.4.4}/letify/providers/shell.py +0 -0
  82. {letify-2.4.3 → letify-2.4.4}/letify/providers/tunnel.py +0 -0
  83. {letify-2.4.3 → letify-2.4.4}/letify/providers/usage.py +0 -0
  84. {letify-2.4.3 → letify-2.4.4}/letify/remoting/__init__.py +0 -0
  85. {letify-2.4.3 → letify-2.4.4}/letify/remoting/capability.py +0 -0
  86. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/__init__.py +0 -0
  87. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/client.py +0 -0
  88. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/cuda.py +0 -0
  89. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/executor.py +0 -0
  90. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/frames.py +0 -0
  91. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/guard.py +0 -0
  92. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/tensor.py +0 -0
  93. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/trace.py +0 -0
  94. {letify-2.4.3 → letify-2.4.4}/letify/remoting/device/value.py +0 -0
  95. {letify-2.4.3 → letify-2.4.4}/letify/remoting/loader.py +0 -0
  96. {letify-2.4.3 → letify-2.4.4}/letify/remoting/probe.py +0 -0
  97. {letify-2.4.3 → letify-2.4.4}/letify/render.py +0 -0
  98. {letify-2.4.3 → letify-2.4.4}/letify/runtime/__init__.py +0 -0
  99. {letify-2.4.3 → letify-2.4.4}/letify/runtime/bootstrap.py +0 -0
  100. {letify-2.4.3 → letify-2.4.4}/letify/runtime/channel.py +0 -0
  101. {letify-2.4.3 → letify-2.4.4}/letify/runtime/lease.py +0 -0
  102. {letify-2.4.3 → letify-2.4.4}/letify/runtime/pool.py +0 -0
  103. {letify-2.4.3 → letify-2.4.4}/letify/runtime/session.py +0 -0
  104. {letify-2.4.3 → letify-2.4.4}/letify/runtime/telemetry.py +0 -0
  105. {letify-2.4.3 → letify-2.4.4}/letify/store/__init__.py +0 -0
  106. {letify-2.4.3 → letify-2.4.4}/letify/store/backends/__init__.py +0 -0
  107. {letify-2.4.3 → letify-2.4.4}/letify/store/backends/filesystem.py +0 -0
  108. {letify-2.4.3 → letify-2.4.4}/letify/store/backends/google_auth.py +0 -0
  109. {letify-2.4.3 → letify-2.4.4}/letify/store/backends/layout.py +0 -0
  110. {letify-2.4.3 → letify-2.4.4}/letify/store/backends/objects.py +0 -0
  111. {letify-2.4.3 → letify-2.4.4}/letify/store/cas.py +0 -0
  112. {letify-2.4.3 → letify-2.4.4}/letify/store/pathdata.py +0 -0
  113. {letify-2.4.3 → letify-2.4.4}/letify/store/sendorder.py +0 -0
  114. {letify-2.4.3 → letify-2.4.4}/letify/store/volume.py +0 -0
  115. {letify-2.4.3 → letify-2.4.4}/letify/stubs.py +0 -0
  116. {letify-2.4.3 → letify-2.4.4}/letify/tools.py +0 -0
  117. {letify-2.4.3 → letify-2.4.4}/letify/transport/__init__.py +0 -0
  118. {letify-2.4.3 → letify-2.4.4}/letify/transport/agent.py +0 -0
  119. {letify-2.4.3 → letify-2.4.4}/letify/transport/announce.py +0 -0
  120. {letify-2.4.3 → letify-2.4.4}/letify/transport/link.py +0 -0
  121. {letify-2.4.3 → letify-2.4.4}/letify/transport/nat.py +0 -0
  122. {letify-2.4.3 → letify-2.4.4}/letify/transport/pipeline.py +0 -0
  123. {letify-2.4.3 → letify-2.4.4}/letify/transport/portmap.py +0 -0
  124. {letify-2.4.3 → letify-2.4.4}/letify/transport/probe.py +0 -0
  125. {letify-2.4.3 → letify-2.4.4}/letify/transport/quic.py +0 -0
  126. {letify-2.4.3 → letify-2.4.4}/letify/transport/rendezvous.py +0 -0
  127. {letify-2.4.3 → letify-2.4.4}/letify/transport/setup.py +0 -0
  128. {letify-2.4.3 → letify-2.4.4}/letify/transport/sshopts.py +0 -0
  129. {letify-2.4.3 → letify-2.4.4}/letify/transport/strategies.py +0 -0
  130. {letify-2.4.3 → letify-2.4.4}/letify-core/.gitignore +0 -0
  131. {letify-2.4.3 → letify-2.4.4}/letify-core/build.py +0 -0
  132. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-agent/Cargo.toml +0 -0
  133. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-agent/src/driver.rs +0 -0
  134. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-agent/src/main.rs +0 -0
  135. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/Cargo.toml +0 -0
  136. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/client.rs +0 -0
  137. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/exports.rs +0 -0
  138. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/image.rs +0 -0
  139. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/lib.rs +0 -0
  140. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/status.rs +0 -0
  141. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-driver/src/table.rs +0 -0
  142. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-quic/Cargo.toml +0 -0
  143. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-quic/src/main.rs +0 -0
  144. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-wire/Cargo.toml +0 -0
  145. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-wire/src/lib.rs +0 -0
  146. {letify-2.4.3 → letify-2.4.4}/letify-core/letify-wire/tests/throughput.rs +0 -0
  147. {letify-2.4.3 → letify-2.4.4}/letify-core/rust-toolchain.toml +0 -0
  148. {letify-2.4.3 → letify-2.4.4}/letify-ext/.vscodeignore +0 -0
  149. {letify-2.4.3 → letify-2.4.4}/letify-ext/LICENSE +0 -0
  150. {letify-2.4.3 → letify-2.4.4}/letify-ext/README.md +0 -0
  151. {letify-2.4.3 → letify-2.4.4}/letify-ext/media/icon.svg +0 -0
  152. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/cli.ts +0 -0
  153. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/extension.ts +0 -0
  154. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/history.ts +0 -0
  155. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/model.ts +0 -0
  156. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/poll.ts +0 -0
  157. {letify-2.4.3 → letify-2.4.4}/letify-ext/src/render.ts +0 -0
  158. {letify-2.4.3 → letify-2.4.4}/letify-ext/test/cli.test.ts +0 -0
  159. {letify-2.4.3 → letify-2.4.4}/letify-ext/test/manifest.test.ts +0 -0
  160. {letify-2.4.3 → letify-2.4.4}/letify-ext/test/model.test.ts +0 -0
  161. {letify-2.4.3 → letify-2.4.4}/letify-ext/test/regressions.test.ts +0 -0
  162. {letify-2.4.3 → letify-2.4.4}/letify-ext/tsconfig.json +0 -0
  163. {letify-2.4.3 → letify-2.4.4}/letify_providers.py +0 -0
  164. {letify-2.4.3 → letify-2.4.4}/scripts/sync-main.sh +0 -0
  165. {letify-2.4.3 → letify-2.4.4}/scripts/version.py +0 -0
  166. {letify-2.4.3 → letify-2.4.4}/tests/conftest.py +0 -0
  167. {letify-2.4.3 → letify-2.4.4}/tests/fake_kaggle_adapter.py +0 -0
  168. {letify-2.4.3 → letify-2.4.4}/tests/fake_modal_adapter.py +0 -0
  169. {letify-2.4.3 → letify-2.4.4}/tests/test_cli.py +0 -0
  170. {letify-2.4.3 → letify-2.4.4}/tests/test_cli_output.py +0 -0
  171. {letify-2.4.3 → letify-2.4.4}/tests/test_colab_keepalive.py +0 -0
  172. {letify-2.4.3 → letify-2.4.4}/tests/test_config.py +0 -0
  173. {letify-2.4.3 → letify-2.4.4}/tests/test_core.py +0 -0
  174. {letify-2.4.3 → letify-2.4.4}/tests/test_data.py +0 -0
  175. {letify-2.4.3 → letify-2.4.4}/tests/test_declare.py +0 -0
  176. {letify-2.4.3 → letify-2.4.4}/tests/test_device.py +0 -0
  177. {letify-2.4.3 → letify-2.4.4}/tests/test_distributed.py +0 -0
  178. {letify-2.4.3 → letify-2.4.4}/tests/test_editor_sessions.py +0 -0
  179. {letify-2.4.3 → letify-2.4.4}/tests/test_elice.py +0 -0
  180. {letify-2.4.3 → letify-2.4.4}/tests/test_frames.py +0 -0
  181. {letify-2.4.3 → letify-2.4.4}/tests/test_install.py +0 -0
  182. {letify-2.4.3 → letify-2.4.4}/tests/test_kaggle.py +0 -0
  183. {letify-2.4.3 → letify-2.4.4}/tests/test_kaggle_cookie.py +0 -0
  184. {letify-2.4.3 → letify-2.4.4}/tests/test_kaggle_link.py +0 -0
  185. {letify-2.4.3 → letify-2.4.4}/tests/test_link_log.py +0 -0
  186. {letify-2.4.3 → letify-2.4.4}/tests/test_notebook.py +0 -0
  187. {letify-2.4.3 → letify-2.4.4}/tests/test_parallel_placement.py +0 -0
  188. {letify-2.4.3 → letify-2.4.4}/tests/test_paths.py +0 -0
  189. {letify-2.4.3 → letify-2.4.4}/tests/test_portmap.py +0 -0
  190. {letify-2.4.3 → letify-2.4.4}/tests/test_protocol.py +0 -0
  191. {letify-2.4.3 → letify-2.4.4}/tests/test_providers.py +0 -0
  192. {letify-2.4.3 → letify-2.4.4}/tests/test_quic.py +0 -0
  193. {letify-2.4.3 → letify-2.4.4}/tests/test_remoting.py +0 -0
  194. {letify-2.4.3 → letify-2.4.4}/tests/test_render.py +0 -0
  195. {letify-2.4.3 → letify-2.4.4}/tests/test_runtime.py +0 -0
  196. {letify-2.4.3 → letify-2.4.4}/tests/test_ship.py +0 -0
  197. {letify-2.4.3 → letify-2.4.4}/tests/test_store.py +0 -0
  198. {letify-2.4.3 → letify-2.4.4}/tests/test_strategies.py +0 -0
  199. {letify-2.4.3 → letify-2.4.4}/tests/test_stubs.py +0 -0
  200. {letify-2.4.3 → letify-2.4.4}/tests/test_transport.py +0 -0
  201. {letify-2.4.3 → letify-2.4.4}/tests/test_tunnel_setup.py +0 -0
  202. {letify-2.4.3 → letify-2.4.4}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: letify
3
- Version: 2.4.3
3
+ Version: 2.4.4
4
4
  Summary: Declarations that become infrastructure.
5
5
  Project-URL: Homepage, https://github.com/DarkPyonix/letify
6
6
  Project-URL: Documentation, https://github.com/DarkPyonix/letify/tree/main/docs
@@ -64,7 +64,7 @@ distributed: Final = Host.distributed
64
64
  # The enum class stays importable from letify.declare.instance for letify's own use.
65
65
  del Host
66
66
 
67
- __version__ = "2.4.3"
67
+ __version__ = "2.4.4"
68
68
 
69
69
 
70
70
  def fetch(tensor): # type: ignore[no-untyped-def]
@@ -365,6 +365,19 @@ def _data_hash_file(path):
365
365
  return hasher.hexdigest(length=16)
366
366
 
367
367
 
368
+ def _data_same_bytes(path, digest):
369
+ """Whether the file still hashes to the digest it is the blob of.
370
+
371
+ Spec "Runtime data cache budget": only the ambiguous case is hashed, a size that
372
+ matches with a new timestamp, so the read is paid where a timestamp cannot decide.
373
+ Never raises; a file that cannot be read is not the file the digest promised.
374
+ """
375
+ try:
376
+ return _data_hash_file(path) == digest
377
+ except OSError:
378
+ return False
379
+
380
+
368
381
  def _data_collect(data, placed):
369
382
  """Commit each file a returned call created or changed at an output into the cache."""
370
383
  import stat
@@ -431,11 +444,25 @@ def _data_check_cache(data, placed):
431
444
  info = os.stat(source)
432
445
  except OSError:
433
446
  continue
434
- if (info.st_size, info.st_mtime_ns) != cached:
435
- try:
436
- os.remove(source)
437
- except OSError:
438
- pass
447
+ if (info.st_size, info.st_mtime_ns) == cached:
448
+ continue
449
+ if info.st_size == cached[0] and _data_same_bytes(source, digest):
450
+ # Spec "Runtime data cache budget": the same bytes with a new timestamp, which
451
+ # is what a file copied back looks like. The digest still holds, so it stays.
452
+ continue
453
+ # Spec "Runtime data cache budget": removing a file a call may still need is worth
454
+ # a line, or it vanishes and only the next failure mentions it.
455
+ print(
456
+ "letify: data cache dropped %s, written through its link (%d bytes at %d, "
457
+ "placed as %d at %d)"
458
+ % (digest, info.st_size, info.st_mtime_ns, cached[0], cached[1]),
459
+ file=sys.stderr,
460
+ flush=True,
461
+ )
462
+ try:
463
+ os.remove(source)
464
+ except OSError:
465
+ pass
439
466
 
440
467
 
441
468
  def _op_data_written(request):
@@ -138,7 +138,7 @@ dependencies = [
138
138
 
139
139
  [[package]]
140
140
  name = "letify-agent"
141
- version = "2.4.3"
141
+ version = "2.4.4"
142
142
  dependencies = [
143
143
  "letify-wire",
144
144
  "libloading",
@@ -146,7 +146,7 @@ dependencies = [
146
146
 
147
147
  [[package]]
148
148
  name = "letify-driver"
149
- version = "2.4.3"
149
+ version = "2.4.4"
150
150
  dependencies = [
151
151
  "letify-wire",
152
152
  ]
@@ -164,7 +164,7 @@ dependencies = [
164
164
 
165
165
  [[package]]
166
166
  name = "letify-wire"
167
- version = "2.4.3"
167
+ version = "2.4.4"
168
168
 
169
169
  [[package]]
170
170
  name = "libc"
@@ -8,7 +8,7 @@ resolver = "3"
8
8
  members = ["letify-wire", "letify-driver", "letify-agent", "letify-quic"]
9
9
 
10
10
  [workspace.package]
11
- version = "2.4.3"
11
+ version = "2.4.4"
12
12
  edition = "2024"
13
13
  license = "MIT"
14
14
  repository = "https://github.com/DarkPyonix/letify"
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "letify-status",
3
- "version": "2.4.3",
3
+ "version": "2.4.4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "letify-status",
9
- "version": "2.4.3",
9
+ "version": "2.4.4",
10
10
  "license": "SEE LICENSE IN LICENSE",
11
11
  "devDependencies": {
12
12
  "@types/node": "^18.19.0",
@@ -2,7 +2,7 @@
2
2
  "name": "letify-status",
3
3
  "displayName": "letify Status",
4
4
  "description": "letify quota and GPU activity in the status bar, read from the letify CLI.",
5
- "version": "2.4.3",
5
+ "version": "2.4.4",
6
6
  "publisher": "darkpyonix",
7
7
  "license": "SEE LICENSE IN LICENSE",
8
8
  "repository": {
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "letify"
3
- version = "2.4.3"
3
+ version = "2.4.4"
4
4
  description = "Declarations that become infrastructure."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -1504,3 +1504,50 @@ def test_a_write_back_leaves_a_directory_that_was_already_there(
1504
1504
  writes(out)
1505
1505
 
1506
1506
  assert (out / "checkpoint-2607" / "kept.txt").read_text(encoding="utf-8") == "mine"
1507
+
1508
+
1509
+ def test_the_cache_says_when_it_drops_a_blob_written_through_its_link() -> None:
1510
+ """Spec "Runtime data cache budget": a dropped blob is reported, not silent.
1511
+
1512
+ A live session spent two days unable to tell this removal from the budget prune,
1513
+ because a blob vanished and only the next failure mentioned it.
1514
+ """
1515
+ from letify.protocol import worker
1516
+
1517
+ at = worker.SOURCE.index("def _data_check_cache")
1518
+ body = worker.SOURCE[at : worker.SOURCE.index("\ndef ", at + 10)]
1519
+ assert "data cache dropped" in body
1520
+ assert "written through its link" in body
1521
+ assert "sys.stderr" in body
1522
+
1523
+
1524
+ def test_a_blob_whose_bytes_are_unchanged_survives_a_new_mtime(launcher_from, project) -> None:
1525
+ """Spec "Runtime data cache budget": dropped on content, not on a timestamp.
1526
+
1527
+ A session restoring its blobs by copying them back had each copy dropped again on its
1528
+ new mtime, so a loop meant to repair the store kept emptying it.
1529
+ """
1530
+ let = streaming(launcher_from, data_first_wave_mib=1024)
1531
+ root = dataset(project, 1, size=1 << 12)
1532
+ target = root / "000.bin"
1533
+
1534
+ @let.function(device=let.providers.lab.CPU, host=letify.remote)
1535
+ def touches(path: Path) -> list[int]:
1536
+ import os as _os
1537
+
1538
+ info = _os.stat(path)
1539
+ # The same bytes, a new timestamp: what a copy back looks like.
1540
+ _os.utime(path, (info.st_atime, info.st_mtime + 120))
1541
+ return [info.st_nlink, _os.stat(path).st_size]
1542
+
1543
+ links, size = touches(target)
1544
+ assert links == 2 and size == 1 << 12
1545
+
1546
+ # The blob is still there, so a second call is told the runtime has it.
1547
+ import io
1548
+ from contextlib import redirect_stderr
1549
+
1550
+ captured = io.StringIO()
1551
+ with redirect_stderr(captured):
1552
+ assert touches(target)[1] == 1 << 12
1553
+ assert "data cache dropped" not in captured.getvalue()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes