letify 2.6.4__tar.gz → 2.6.5__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.
- {letify-2.6.4 → letify-2.6.5}/PKG-INFO +1 -1
- {letify-2.6.4 → letify-2.6.5}/docs/SPEC.md +13 -1
- {letify-2.6.4 → letify-2.6.5}/letify/__init__.py +1 -1
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/session.py +33 -10
- {letify-2.6.4 → letify-2.6.5}/letify-core/Cargo.lock +3 -3
- {letify-2.6.4 → letify-2.6.5}/letify-core/Cargo.toml +1 -1
- {letify-2.6.4 → letify-2.6.5}/letify-ext/package-lock.json +2 -2
- {letify-2.6.4 → letify-2.6.5}/letify-ext/package.json +1 -1
- {letify-2.6.4 → letify-2.6.5}/pyproject.toml +1 -1
- {letify-2.6.4 → letify-2.6.5}/tests/test_runtime.py +54 -0
- {letify-2.6.4 → letify-2.6.5}/.github/workflows/ci.yml +0 -0
- {letify-2.6.4 → letify-2.6.5}/.github/workflows/docs.yml +0 -0
- {letify-2.6.4 → letify-2.6.5}/.github/workflows/publish.yml +0 -0
- {letify-2.6.4 → letify-2.6.5}/.gitignore +0 -0
- {letify-2.6.4 → letify-2.6.5}/.researchtree +0 -0
- {letify-2.6.4 → letify-2.6.5}/CLAUDE.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/LICENSE +0 -0
- {letify-2.6.4 → letify-2.6.5}/PROJECT.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/COMPONENT.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/INTENT.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/NETWORK.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/01-getting-started.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/02-providers.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/03-execution-modes.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/04-environments-and-data.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/05-concurrency.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/06-cost.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/07-troubleshooting.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/08-releasing.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/01-/354/213/234/354/236/221/355/225/230/352/270/260.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/02-/355/224/204/353/241/234/353/260/224/354/235/264/353/215/224.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/03-/354/213/244/355/226/211-/353/260/251/354/213/235.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/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
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/05-/353/217/231/354/213/234-/354/213/244/355/226/211.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/06-/353/271/204/354/232/251.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/07-/353/254/270/354/240/234-/355/225/264/352/262/260.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/08-/353/246/264/353/246/254/354/212/244.md" +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/guide/ko/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/locales/README_ko.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/docs/mkdocs.yml +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/.letify.example +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/00_smoke.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/01_sweep.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/02_resume.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/03_watch.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/README_ko.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/common.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/examples/nvfp4-lora/recipe.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/cli.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/inventory.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/login.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/schema.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/secrets.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/config/writer.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/cache.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/env.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/function.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/instance.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/reduce.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/declare/replicas.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/errors.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/install.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/launcher.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/paths.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/codec.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/driver.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/handle.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/wire.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/protocol/worker.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/base.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/colab.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/colab_files.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/colab_keepalive.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/elice.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/kaggle.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/kaggle_adapter.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/local.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/modal.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/modal_adapter.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/naming.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/shell.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/tunnel.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/providers/usage.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/capability.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/client.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/cuda.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/executor.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/frames.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/guard.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/tensor.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/trace.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/device/value.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/loader.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/remoting/probe.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/render.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/bootstrap.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/broker.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/channel.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/lease.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/pool.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/runtime/telemetry.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/backends/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/backends/filesystem.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/backends/google_auth.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/backends/layout.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/backends/objects.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/cas.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/pathdata.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/sendorder.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/store/volume.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/stubs.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/tools.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/__init__.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/agent.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/announce.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/link.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/nat.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/pipeline.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/portmap.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/probe.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/quic.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/rendezvous.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/setup.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/sshopts.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify/transport/strategies.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/.gitignore +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/build.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-agent/Cargo.toml +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-agent/src/driver.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-agent/src/main.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/Cargo.toml +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/client.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/exports.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/image.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/lib.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/status.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-driver/src/table.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-quic/Cargo.toml +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-quic/src/main.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-wire/Cargo.toml +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-wire/src/lib.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/letify-wire/tests/throughput.rs +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-core/rust-toolchain.toml +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/.vscodeignore +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/LICENSE +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/README.md +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/media/icon.svg +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/cli.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/extension.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/history.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/model.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/poll.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/src/render.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/test/cli.test.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/test/manifest.test.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/test/model.test.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/test/regressions.test.ts +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify-ext/tsconfig.json +0 -0
- {letify-2.6.4 → letify-2.6.5}/letify_providers.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/scripts/sync-main.sh +0 -0
- {letify-2.6.4 → letify-2.6.5}/scripts/version.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/conftest.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/fake_kaggle_adapter.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/fake_modal_adapter.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_cli.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_cli_output.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_colab_keepalive.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_config.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_core.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_data.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_data_streaming.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_declare.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_device.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_distributed.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_editor_sessions.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_elice.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_frames.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_install.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_kaggle.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_kaggle_cookie.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_kaggle_link.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_link_log.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_notebook.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_parallel_placement.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_paths.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_portmap.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_protocol.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_providers.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_quic.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_remoting.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_render.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_ship.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_store.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_strategies.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_stubs.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_transport.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_tunnel_setup.py +0 -0
- {letify-2.6.4 → letify-2.6.5}/tests/test_version.py +0 -0
|
@@ -1063,6 +1063,16 @@ less time than the build did. The archive is smaller than the directory, so the
|
|
|
1063
1063
|
overstates the transfer and errs toward building, which is the cheaper of the two to get wrong. The
|
|
1064
1064
|
ref records the build seconds beside the archive.
|
|
1065
1065
|
|
|
1066
|
+
With no measured rate for the link, letify packs only when that directory is at most 1 GiB, and says
|
|
1067
|
+
why when it declines. Packing moves the archive as one payload that the worker and the client each
|
|
1068
|
+
hold whole, so a tree of several gigabytes is held twice in memory with nothing reporting progress:
|
|
1069
|
+
measured on a Modal A10, a 7.2 GB environment sat in that step for over 30 minutes while the session
|
|
1070
|
+
billed. Packing unconditionally was right while only an ephemeral provider packed, where the
|
|
1071
|
+
alternative was a certain full rebuild; a provider that keeps its workspace root and builds its venv
|
|
1072
|
+
elsewhere reaches the same branch, and there the alternative is a sync that may take two minutes.
|
|
1073
|
+
Making the pack stream rather than hold the archive whole is the fix that removes this limit, and
|
|
1074
|
+
until then the limit is what keeps the step from costing more than it saves.
|
|
1075
|
+
|
|
1066
1076
|
A later session restores only when the archive's size over the link measured for that session would
|
|
1067
1077
|
take less than the recorded build. Otherwise it syncs, and says why:
|
|
1068
1078
|
`letify: <runtime>: syncing rather than restoring, because 3.0 GB over 13.6 MiB/s is 210 s against a
|
|
@@ -1076,7 +1086,9 @@ the kind this rule exists to catch, and the session that syncs instead both reco
|
|
|
1076
1086
|
decides afresh whether the archive is worth keeping. The cost of that is one sync; the cost of
|
|
1077
1087
|
trusting it is the transfer this rule was measured to avoid.
|
|
1078
1088
|
|
|
1079
|
-
|
|
1089
|
+
Packing is said, because it is a step that can take minutes and had nothing reporting it:
|
|
1090
|
+
`letify: <runtime>: caching the environment, 2.1 GB`. Skipping it is said too, because it is a
|
|
1091
|
+
decision letify made rather than a thing that did not
|
|
1080
1092
|
happen: `letify: <runtime>: not caching the environment, because 9.4 GB over 13.6 MiB/s is 659 s
|
|
1081
1093
|
against a 34 s build`.
|
|
1082
1094
|
|
|
@@ -47,6 +47,11 @@ if TYPE_CHECKING:
|
|
|
47
47
|
#: The total size of argument blob files kept under a persistent workspace root, 32 GiB.
|
|
48
48
|
BLOB_DISK_LIMIT = 32 * 1024**3
|
|
49
49
|
|
|
50
|
+
#: The largest project directory letify packs into an environment archive when it has no
|
|
51
|
+
#: measured rate to judge the transfer by. Spec "Materializing into a runtime": the pack
|
|
52
|
+
#: moves the archive as one payload the worker and the client each hold whole.
|
|
53
|
+
PACK_WITHOUT_A_RATE = 1024**3
|
|
54
|
+
|
|
50
55
|
|
|
51
56
|
def _against_the_build(size: int, rate: float, build_s: float) -> str:
|
|
52
57
|
"""The three numbers both cache decisions weigh, as they are reported.
|
|
@@ -185,6 +190,11 @@ class Runtime:
|
|
|
185
190
|
self.provider.env_root or self.workspace or self.provider.workspace_root, self.env
|
|
186
191
|
)
|
|
187
192
|
if self._worth_packing(root):
|
|
193
|
+
size = self._tree_size(root)
|
|
194
|
+
self._say(
|
|
195
|
+
"caching the environment"
|
|
196
|
+
+ (f", {size / 1_000_000_000:.1f} GB" if size else "")
|
|
197
|
+
)
|
|
188
198
|
self._environment_volumes()[0].cache_env_from(
|
|
189
199
|
self, self.env, root, platform=self.platform, build_s=self.env_build_s
|
|
190
200
|
)
|
|
@@ -687,25 +697,38 @@ class Runtime:
|
|
|
687
697
|
return None
|
|
688
698
|
return float(rate)
|
|
689
699
|
|
|
700
|
+
def _tree_size(self, root: str) -> int | None:
|
|
701
|
+
"""The byte size of the project directory on the runtime, or None when unknown."""
|
|
702
|
+
from . import bootstrap
|
|
703
|
+
|
|
704
|
+
try:
|
|
705
|
+
size = self.eval(bootstrap.tree_size_source(root), timeout=120)
|
|
706
|
+
except Exception:
|
|
707
|
+
return None
|
|
708
|
+
return size if isinstance(size, int) and size > 0 else None
|
|
709
|
+
|
|
690
710
|
def _worth_packing(self, root: str) -> bool:
|
|
691
711
|
"""Whether moving the project directory over this session's link beats the build.
|
|
692
712
|
|
|
693
713
|
Spec "Materializing into a runtime": the directory is larger than the archive it
|
|
694
|
-
would become, so this overstates the transfer and errs toward building.
|
|
695
|
-
|
|
714
|
+
would become, so this overstates the transfer and errs toward building. With no
|
|
715
|
+
measured rate it packs only what letify is willing to hold in memory, because the
|
|
716
|
+
pack moves the archive as one payload the worker and the client each hold whole.
|
|
696
717
|
"""
|
|
697
718
|
if self.env_build_s is None:
|
|
698
719
|
return True
|
|
699
720
|
rate = self._measured_rate()
|
|
721
|
+
size = self._tree_size(root)
|
|
700
722
|
if rate is None:
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
723
|
+
if size is None or size <= PACK_WITHOUT_A_RATE:
|
|
724
|
+
return True
|
|
725
|
+
self._say(
|
|
726
|
+
f"not caching the environment, because {size / 1_000_000_000:.1f} GB is over "
|
|
727
|
+
f"the {PACK_WITHOUT_A_RATE / 1024**3:.1f} GiB letify will hold in memory and "
|
|
728
|
+
f"this link was never measured"
|
|
729
|
+
)
|
|
730
|
+
return False
|
|
731
|
+
if size is None:
|
|
709
732
|
return True
|
|
710
733
|
if (size / rate) < self.env_build_s:
|
|
711
734
|
return True
|
|
@@ -138,7 +138,7 @@ dependencies = [
|
|
|
138
138
|
|
|
139
139
|
[[package]]
|
|
140
140
|
name = "letify-agent"
|
|
141
|
-
version = "2.6.
|
|
141
|
+
version = "2.6.5"
|
|
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.6.
|
|
149
|
+
version = "2.6.5"
|
|
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.6.
|
|
167
|
+
version = "2.6.5"
|
|
168
168
|
|
|
169
169
|
[[package]]
|
|
170
170
|
name = "libc"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letify-status",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "letify-status",
|
|
9
|
-
"version": "2.6.
|
|
9
|
+
"version": "2.6.5",
|
|
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.6.
|
|
5
|
+
"version": "2.6.5",
|
|
6
6
|
"publisher": "darkpyonix",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"repository": {
|
|
@@ -2593,3 +2593,57 @@ def test_a_persistent_provider_that_builds_its_venv_elsewhere_still_archives(
|
|
|
2593
2593
|
finally:
|
|
2594
2594
|
runtime.shutdown()
|
|
2595
2595
|
assert volume.cached_env(env, platform), "nothing was archived, so the next session resyncs"
|
|
2596
|
+
|
|
2597
|
+
|
|
2598
|
+
def test_a_large_environment_is_not_packed_when_the_link_was_never_measured(
|
|
2599
|
+
uv_project: Path, tmp_path: Path, monkeypatch, capsys
|
|
2600
|
+
) -> None:
|
|
2601
|
+
"""Spec "Materializing into a runtime": the pack holds the archive whole, twice.
|
|
2602
|
+
|
|
2603
|
+
2.6.3 made a provider that keeps its workspace root archive like an ephemeral one, and
|
|
2604
|
+
that branch packed unconditionally with no measured rate. On a Modal A10 a 7.2 GB
|
|
2605
|
+
environment sat in the pack for over 30 minutes, silently, while the session billed.
|
|
2606
|
+
"""
|
|
2607
|
+
from letify.runtime import session as sessionmod
|
|
2608
|
+
|
|
2609
|
+
provider = provider_of(PreparingLocal, "lab", persistent=False)
|
|
2610
|
+
volume = provider.volume(
|
|
2611
|
+
"cache", backend="filesystem", root=str(tmp_path / "store"), mount=str(tmp_path / "mount")
|
|
2612
|
+
)
|
|
2613
|
+
env = Env()
|
|
2614
|
+
instance = Instance(provider, gpu=None)._placed("remote")
|
|
2615
|
+
# No measured rate, as a Modal sandbox has none, and a tree over the limit.
|
|
2616
|
+
monkeypatch.setattr(sessionmod.Runtime, "_measured_rate", lambda self: None)
|
|
2617
|
+
monkeypatch.setattr(
|
|
2618
|
+
sessionmod.Runtime, "_tree_size", lambda self, root: 8 * 1024**3
|
|
2619
|
+
)
|
|
2620
|
+
runtime = provider.start(instance, env, name="lab-1", volumes=(volume,))
|
|
2621
|
+
try:
|
|
2622
|
+
platform = runtime.platform
|
|
2623
|
+
finally:
|
|
2624
|
+
runtime.shutdown()
|
|
2625
|
+
assert volume.cached_env(env, platform) is None
|
|
2626
|
+
err = capsys.readouterr().err
|
|
2627
|
+
assert "over the 1.0 GiB letify will hold in memory" in err, err
|
|
2628
|
+
|
|
2629
|
+
|
|
2630
|
+
def test_a_small_environment_is_still_packed_and_says_so(
|
|
2631
|
+
uv_project: Path, tmp_path: Path, monkeypatch, capsys
|
|
2632
|
+
) -> None:
|
|
2633
|
+
"""Spec "Materializing into a runtime": the pack can take minutes, so it is said."""
|
|
2634
|
+
from letify.runtime import session as sessionmod
|
|
2635
|
+
|
|
2636
|
+
provider = provider_of(PreparingLocal, "lab", persistent=False)
|
|
2637
|
+
volume = provider.volume(
|
|
2638
|
+
"cache", backend="filesystem", root=str(tmp_path / "store"), mount=str(tmp_path / "mount")
|
|
2639
|
+
)
|
|
2640
|
+
env = Env()
|
|
2641
|
+
instance = Instance(provider, gpu=None)._placed("remote")
|
|
2642
|
+
monkeypatch.setattr(sessionmod.Runtime, "_measured_rate", lambda self: None)
|
|
2643
|
+
runtime = provider.start(instance, env, name="lab-1", volumes=(volume,))
|
|
2644
|
+
try:
|
|
2645
|
+
platform = runtime.platform
|
|
2646
|
+
finally:
|
|
2647
|
+
runtime.shutdown()
|
|
2648
|
+
assert volume.cached_env(env, platform)
|
|
2649
|
+
assert "caching the environment" in capsys.readouterr().err
|
|
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
|
{letify-2.6.4 → letify-2.6.5}/docs/guide/ko/01-/354/213/234/354/236/221/355/225/230/352/270/260.md"
RENAMED
|
File without changes
|
|
File without changes
|
{letify-2.6.4 → letify-2.6.5}/docs/guide/ko/03-/354/213/244/355/226/211-/353/260/251/354/213/235.md"
RENAMED
|
File without changes
|
|
File without changes
|
{letify-2.6.4 → letify-2.6.5}/docs/guide/ko/05-/353/217/231/354/213/234-/354/213/244/355/226/211.md"
RENAMED
|
File without changes
|
|
File without changes
|
{letify-2.6.4 → letify-2.6.5}/docs/guide/ko/07-/353/254/270/354/240/234-/355/225/264/352/262/260.md"
RENAMED
|
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
|
|
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
|