letify 2.6.5__tar.gz → 2.6.6__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.5 → letify-2.6.6}/PKG-INFO +1 -1
- {letify-2.6.5 → letify-2.6.6}/docs/SPEC.md +8 -0
- {letify-2.6.5 → letify-2.6.6}/letify/__init__.py +1 -1
- {letify-2.6.5 → letify-2.6.6}/letify/store/pathdata.py +52 -2
- {letify-2.6.5 → letify-2.6.6}/letify-core/Cargo.lock +3 -3
- {letify-2.6.5 → letify-2.6.6}/letify-core/Cargo.toml +1 -1
- {letify-2.6.5 → letify-2.6.6}/letify-ext/package-lock.json +2 -2
- {letify-2.6.5 → letify-2.6.6}/letify-ext/package.json +1 -1
- {letify-2.6.5 → letify-2.6.6}/pyproject.toml +1 -1
- {letify-2.6.5 → letify-2.6.6}/tests/test_data_streaming.py +63 -0
- {letify-2.6.5 → letify-2.6.6}/.github/workflows/ci.yml +0 -0
- {letify-2.6.5 → letify-2.6.6}/.github/workflows/docs.yml +0 -0
- {letify-2.6.5 → letify-2.6.6}/.github/workflows/publish.yml +0 -0
- {letify-2.6.5 → letify-2.6.6}/.gitignore +0 -0
- {letify-2.6.5 → letify-2.6.6}/.researchtree +0 -0
- {letify-2.6.5 → letify-2.6.6}/CLAUDE.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/LICENSE +0 -0
- {letify-2.6.5 → letify-2.6.6}/PROJECT.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/COMPONENT.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/INTENT.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/NETWORK.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/01-getting-started.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/02-providers.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/03-execution-modes.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/04-environments-and-data.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/05-concurrency.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/06-cost.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/07-troubleshooting.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/08-releasing.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/ko/01-/354/213/234/354/236/221/355/225/230/352/270/260.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/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.5 → letify-2.6.6}/docs/guide/ko/03-/354/213/244/355/226/211-/353/260/251/354/213/235.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/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.5 → letify-2.6.6}/docs/guide/ko/05-/353/217/231/354/213/234-/354/213/244/355/226/211.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/ko/06-/353/271/204/354/232/251.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/ko/07-/353/254/270/354/240/234-/355/225/264/352/262/260.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/ko/08-/353/246/264/353/246/254/354/212/244.md" +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/guide/ko/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/locales/README_ko.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/docs/mkdocs.yml +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/.letify.example +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/00_smoke.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/01_sweep.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/02_resume.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/03_watch.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/README_ko.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/common.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/examples/nvfp4-lora/recipe.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/cli.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/inventory.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/login.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/schema.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/secrets.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/config/writer.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/cache.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/env.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/function.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/instance.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/reduce.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/declare/replicas.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/errors.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/install.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/launcher.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/paths.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/codec.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/driver.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/handle.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/wire.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/protocol/worker.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/base.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/colab.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/colab_files.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/colab_keepalive.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/elice.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/kaggle.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/kaggle_adapter.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/local.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/modal.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/modal_adapter.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/naming.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/shell.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/tunnel.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/providers/usage.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/capability.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/client.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/cuda.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/executor.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/frames.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/guard.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/tensor.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/trace.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/device/value.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/loader.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/remoting/probe.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/render.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/bootstrap.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/broker.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/channel.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/lease.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/pool.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/session.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/runtime/telemetry.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/backends/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/backends/filesystem.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/backends/google_auth.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/backends/layout.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/backends/objects.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/cas.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/sendorder.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/store/volume.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/stubs.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/tools.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/__init__.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/agent.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/announce.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/link.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/nat.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/pipeline.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/portmap.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/probe.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/quic.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/rendezvous.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/setup.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/sshopts.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify/transport/strategies.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/.gitignore +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/build.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-agent/Cargo.toml +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-agent/src/driver.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-agent/src/main.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/Cargo.toml +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/client.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/exports.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/image.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/lib.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/status.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-driver/src/table.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-quic/Cargo.toml +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-quic/src/main.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-wire/Cargo.toml +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-wire/src/lib.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/letify-wire/tests/throughput.rs +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-core/rust-toolchain.toml +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/.vscodeignore +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/LICENSE +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/README.md +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/media/icon.svg +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/cli.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/extension.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/history.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/model.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/poll.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/src/render.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/test/cli.test.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/test/manifest.test.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/test/model.test.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/test/regressions.test.ts +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify-ext/tsconfig.json +0 -0
- {letify-2.6.5 → letify-2.6.6}/letify_providers.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/scripts/sync-main.sh +0 -0
- {letify-2.6.5 → letify-2.6.6}/scripts/version.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/conftest.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/fake_kaggle_adapter.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/fake_modal_adapter.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_cli.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_cli_output.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_colab_keepalive.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_config.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_core.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_data.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_declare.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_device.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_distributed.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_editor_sessions.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_elice.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_frames.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_install.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_kaggle.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_kaggle_cookie.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_kaggle_link.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_link_log.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_notebook.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_parallel_placement.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_paths.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_portmap.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_protocol.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_providers.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_quic.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_remoting.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_render.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_runtime.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_ship.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_store.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_strategies.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_stubs.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_transport.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_tunnel_setup.py +0 -0
- {letify-2.6.5 → letify-2.6.6}/tests/test_version.py +0 -0
|
@@ -1365,6 +1365,14 @@ A call that detected data prints one line on standard error:
|
|
|
1365
1365
|
|
|
1366
1366
|
The line is printed when the call has at least one input file, after the call's outcome is known, because what was sent during the call is not known before then. Sizes are in MiB with one decimal. `<the bucket>` is named when the bytes come from the bucket. `before the call` counts the first wave of The send order and the first wave, `during it` counts what the background sender delivered while the call ran, and the rate covers both. `first access waited` is the wall clock time in which at least one reader was blocked on a blob that had not arrived, and it is `0.0` when the send order was right.
|
|
1367
1367
|
|
|
1368
|
+
The bytes of a blob still on the way are reported too, because the rate counts only blobs that
|
|
1369
|
+
completed and a single large blob in flight therefore reads as `0.0 MiB/s` however fast it is moving.
|
|
1370
|
+
A line whose dropped clause names files ends with how far they got,
|
|
1371
|
+
`, 3 files 14985.8 MiB dropped when the call ended, 4512.0 MiB of them had arrived`, and the
|
|
1372
|
+
background sender draws the same `uploading` progress line the first wave draws, which it did not
|
|
1373
|
+
before: a blob sent beside the call reported nothing at all, so a transfer in progress and a transfer
|
|
1374
|
+
that had never started looked the same. Both cost a reader an afternoon.
|
|
1375
|
+
|
|
1368
1376
|
`already on` counts the blobs the runtime answered `data_have` with, not what is left after subtracting what was sent. The two differ whenever a blob was neither already there nor fully sent, and subtracting reports such a blob as having been there all along, which is the opposite of what happened. A blob the background sender was still carrying when the call returned is one of those, so the line ends with `, <files> files <size> dropped when the call ended` whenever there is one, and omits that clause when there is none. A dropped blob is not a failure: the call is over and its bytes are no longer needed, which is why the sender is cancelled. It is reported because a line that accounts for every detected file except the dropped ones cannot be read.
|
|
1369
1377
|
|
|
1370
1378
|
Cancelling the sender removes the partial file of a blob it was writing, in the runtime's blob cache as well as a parallel put's staged ranges. A full sized partial left behind counts against the cache budget and is never completed, because the digest it would be sealed under is only written when every range has arrived.
|
|
@@ -485,6 +485,32 @@ def _mib(size: int) -> str:
|
|
|
485
485
|
return f"{size / (1 << 20):.1f} MiB"
|
|
486
486
|
|
|
487
487
|
|
|
488
|
+
class _Carried:
|
|
489
|
+
"""One blob's progress, recorded on the stream and forwarded to the real meter.
|
|
490
|
+
|
|
491
|
+
Spec "Data log line": the bytes of a blob still on the way are reported, and only the
|
|
492
|
+
blob's own sender knows how far it got.
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
def __init__(self, stream: Any, digest: str, meter: Any) -> None:
|
|
496
|
+
self._stream = stream
|
|
497
|
+
self._digest = digest
|
|
498
|
+
self._meter = meter
|
|
499
|
+
|
|
500
|
+
def update(self, done: int) -> None:
|
|
501
|
+
self._stream.carried[self._digest] = done
|
|
502
|
+
if self._meter is not None:
|
|
503
|
+
self._meter.update(done)
|
|
504
|
+
|
|
505
|
+
def advance(self, size: int) -> None:
|
|
506
|
+
if self._meter is not None:
|
|
507
|
+
self._meter.advance(size)
|
|
508
|
+
|
|
509
|
+
def finish(self) -> None:
|
|
510
|
+
if self._meter is not None:
|
|
511
|
+
self._meter.finish()
|
|
512
|
+
|
|
513
|
+
|
|
488
514
|
class _Uploaded:
|
|
489
515
|
"""Bytes sent so far, drawn as a progress line when the upload is large.
|
|
490
516
|
|
|
@@ -626,6 +652,10 @@ class Stream:
|
|
|
626
652
|
#: Digests whose send finished. ``_sent`` holds the ones it began, so a blob the
|
|
627
653
|
#: sender was carrying when the call ended is in one and not the other.
|
|
628
654
|
self.done: set[str] = set()
|
|
655
|
+
#: Bytes of each blob handed to the runtime, including one still on the way.
|
|
656
|
+
self.carried: dict[str, int] = {}
|
|
657
|
+
#: The meter for blobs sent beside the call, spec "Data log line".
|
|
658
|
+
self._meter: _Uploaded | None = None
|
|
629
659
|
self.before_files = self.before_bytes = 0
|
|
630
660
|
self.during_files = self.during_bytes = 0
|
|
631
661
|
self.before_seconds = self.during_seconds = 0.0
|
|
@@ -750,9 +780,22 @@ class Stream:
|
|
|
750
780
|
digest = self._take()
|
|
751
781
|
if digest is None:
|
|
752
782
|
return False
|
|
753
|
-
self._send(digest,
|
|
783
|
+
self._send(digest, self._beside())
|
|
754
784
|
return True
|
|
755
785
|
|
|
786
|
+
def _beside(self) -> _Uploaded:
|
|
787
|
+
"""The meter for blobs sent beside the call, made at the first one.
|
|
788
|
+
|
|
789
|
+
Spec "Data log line": without one, a blob sent beside the call reported nothing,
|
|
790
|
+
so a transfer in progress and one that never started looked the same.
|
|
791
|
+
"""
|
|
792
|
+
with self._lock:
|
|
793
|
+
if self._meter is None:
|
|
794
|
+
left = [d for d in self.plan.missing if d not in self.done]
|
|
795
|
+
total = sum(self.plan.weight(digest)[1] for digest in left)
|
|
796
|
+
self._meter = _Uploaded(len(left), total)
|
|
797
|
+
return self._meter
|
|
798
|
+
|
|
756
799
|
def _take(self) -> str | None:
|
|
757
800
|
with self._lock:
|
|
758
801
|
while self._queue:
|
|
@@ -916,6 +959,7 @@ class Stream:
|
|
|
916
959
|
size = self.plan.sizes.get(digest, 0)
|
|
917
960
|
local = self.plan.files[digest]
|
|
918
961
|
bucket = self.plan.bucket
|
|
962
|
+
meter = _Carried(self, digest, meter)
|
|
919
963
|
uploaded = True
|
|
920
964
|
if bucket is None:
|
|
921
965
|
# The cost of opening the connections is weighed only while the pool is cold:
|
|
@@ -1014,9 +1058,13 @@ class Stream:
|
|
|
1014
1058
|
missing_weight = [plan.weight(digest) for digest in plan.missing]
|
|
1015
1059
|
already_files = plan.detected_files - sum(files for files, _b in missing_weight)
|
|
1016
1060
|
already_bytes = plan.detected_bytes - sum(total for _f, total in missing_weight)
|
|
1017
|
-
|
|
1061
|
+
dropped = self.dropped
|
|
1062
|
+
dropped_weight = [plan.weight(digest) for digest in dropped]
|
|
1018
1063
|
dropped_files = sum(files for files, _b in dropped_weight)
|
|
1019
1064
|
dropped_bytes = sum(total for _f, total in dropped_weight)
|
|
1065
|
+
# Spec "Data log line": the rate counts only blobs that completed, so a large one
|
|
1066
|
+
# in flight reads as 0.0 MiB/s however fast it is moving.
|
|
1067
|
+
arrived = sum(self.carried.get(digest, 0) for digest in dropped)
|
|
1020
1068
|
line = (
|
|
1021
1069
|
f"data {plan.detected_files} files {_mib(plan.detected_bytes)} detected, "
|
|
1022
1070
|
f"{already_files} files {_mib(already_bytes)} already on {plan.where}, "
|
|
@@ -1029,6 +1077,8 @@ class Stream:
|
|
|
1029
1077
|
line += (
|
|
1030
1078
|
f", {dropped_files} files {_mib(dropped_bytes)} dropped when the call ended"
|
|
1031
1079
|
)
|
|
1080
|
+
if arrived:
|
|
1081
|
+
line += f", {_mib(arrived)} of them had arrived"
|
|
1032
1082
|
install.log(line)
|
|
1033
1083
|
if stats.get("files"):
|
|
1034
1084
|
install.log(
|
|
@@ -138,7 +138,7 @@ dependencies = [
|
|
|
138
138
|
|
|
139
139
|
[[package]]
|
|
140
140
|
name = "letify-agent"
|
|
141
|
-
version = "2.6.
|
|
141
|
+
version = "2.6.6"
|
|
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.6"
|
|
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.6"
|
|
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.6",
|
|
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.6",
|
|
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.6",
|
|
6
6
|
"publisher": "darkpyonix",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"repository": {
|
|
@@ -1757,3 +1757,66 @@ def test_a_blob_that_never_starts_still_times_out_and_says_how_far_it_got(
|
|
|
1757
1757
|
message = str(failure.value)
|
|
1758
1758
|
assert "did not arrive" in message, message
|
|
1759
1759
|
assert "arrived" in message, message
|
|
1760
|
+
|
|
1761
|
+
|
|
1762
|
+
def test_a_blob_sent_beside_the_call_reports_its_bytes(
|
|
1763
|
+
let, cpu, project, capsys, monkeypatch
|
|
1764
|
+
) -> None:
|
|
1765
|
+
"""Spec "Data log line": a transfer in progress read the same as one never started.
|
|
1766
|
+
|
|
1767
|
+
KICT's 11.3 GB table was moving the whole time and the line said 0.0 MiB/s, because
|
|
1768
|
+
the rate counts only blobs that completed and the background sender was given no
|
|
1769
|
+
meter at all.
|
|
1770
|
+
"""
|
|
1771
|
+
from letify.store import pathdata
|
|
1772
|
+
|
|
1773
|
+
big = project / "beside.bin"
|
|
1774
|
+
big.write_bytes(b"q" * (3 << 20))
|
|
1775
|
+
monkeypatch.setattr(pathdata, "FIRST_WAVE_MIB", 0.0)
|
|
1776
|
+
monkeypatch.setattr(pathdata, "FIRST_WAVE_FILES", 0)
|
|
1777
|
+
monkeypatch.setattr(pathdata, "WAIT_TIMEOUT_S", 4.0)
|
|
1778
|
+
def stalled(runtime, pipeline, blobs, digest, local, size, meter):
|
|
1779
|
+
# A third of the blob is reported as handed over and then the send stops, which
|
|
1780
|
+
# is the shape of a blob the sender was carrying when the call ended.
|
|
1781
|
+
meter.update(1 << 20)
|
|
1782
|
+
raise RuntimeFailure("the transfer stream failed")
|
|
1783
|
+
|
|
1784
|
+
monkeypatch.setattr(pathdata, "_put", stalled)
|
|
1785
|
+
|
|
1786
|
+
@let.function(device=cpu, host=letify.remote, retries=0)
|
|
1787
|
+
def peek(path) -> int:
|
|
1788
|
+
return len(path.read_bytes())
|
|
1789
|
+
|
|
1790
|
+
with pytest.raises(RuntimeFailure):
|
|
1791
|
+
peek(path=big)
|
|
1792
|
+
lines = [
|
|
1793
|
+
text
|
|
1794
|
+
for text in capsys.readouterr().err.splitlines()
|
|
1795
|
+
if text.startswith("letify: data ") and "detected," in text
|
|
1796
|
+
]
|
|
1797
|
+
assert lines
|
|
1798
|
+
assert "dropped when the call ended" in lines[0], lines[0]
|
|
1799
|
+
assert "had arrived" in lines[0], lines[0]
|
|
1800
|
+
|
|
1801
|
+
|
|
1802
|
+
def test_a_large_blob_beside_the_call_draws_the_progress_line(
|
|
1803
|
+
let, cpu, project, capsys, monkeypatch
|
|
1804
|
+
) -> None:
|
|
1805
|
+
"""Spec "Data log line": the background sender draws the same line the wave draws.
|
|
1806
|
+
|
|
1807
|
+
KICT reported no 'uploading' line at all while 11.3 GB moved beside a call, because
|
|
1808
|
+
the background sender was given no meter.
|
|
1809
|
+
"""
|
|
1810
|
+
from letify.store import pathdata
|
|
1811
|
+
|
|
1812
|
+
big = project / "wide.bin"
|
|
1813
|
+
big.write_bytes(b"w" * (70 << 20))
|
|
1814
|
+
monkeypatch.setattr(pathdata, "FIRST_WAVE_MIB", 0.0)
|
|
1815
|
+
monkeypatch.setattr(pathdata, "FIRST_WAVE_FILES", 0)
|
|
1816
|
+
|
|
1817
|
+
@let.function(device=cpu, host=letify.remote)
|
|
1818
|
+
def read_it(path) -> int:
|
|
1819
|
+
return len(path.read_bytes())
|
|
1820
|
+
|
|
1821
|
+
assert read_it(path=big) == 70 << 20
|
|
1822
|
+
assert "uploading" 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.5 → letify-2.6.6}/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.5 → letify-2.6.6}/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.5 → letify-2.6.6}/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.5 → letify-2.6.6}/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
|