letify 2.4.5__tar.gz → 2.4.7__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.4.5 → letify-2.4.7}/PKG-INFO +1 -1
- {letify-2.4.5 → letify-2.4.7}/letify/__init__.py +1 -1
- {letify-2.4.5 → letify-2.4.7}/letify/providers/kaggle.py +22 -4
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/bootstrap.py +17 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/session.py +21 -2
- {letify-2.4.5 → letify-2.4.7}/letify-core/Cargo.lock +3 -3
- {letify-2.4.5 → letify-2.4.7}/letify-core/Cargo.toml +1 -1
- {letify-2.4.5 → letify-2.4.7}/letify-ext/package-lock.json +2 -2
- {letify-2.4.5 → letify-2.4.7}/letify-ext/package.json +1 -1
- {letify-2.4.5 → letify-2.4.7}/pyproject.toml +1 -1
- {letify-2.4.5 → letify-2.4.7}/tests/test_kaggle.py +81 -1
- {letify-2.4.5 → letify-2.4.7}/tests/test_kaggle_link.py +41 -0
- {letify-2.4.5 → letify-2.4.7}/.github/workflows/ci.yml +0 -0
- {letify-2.4.5 → letify-2.4.7}/.github/workflows/docs.yml +0 -0
- {letify-2.4.5 → letify-2.4.7}/.github/workflows/publish.yml +0 -0
- {letify-2.4.5 → letify-2.4.7}/.gitignore +0 -0
- {letify-2.4.5 → letify-2.4.7}/.researchtree +0 -0
- {letify-2.4.5 → letify-2.4.7}/LICENSE +0 -0
- {letify-2.4.5 → letify-2.4.7}/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/01-getting-started.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/02-providers.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/03-execution-modes.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/04-environments-and-data.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/05-concurrency.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/06-cost.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/07-troubleshooting.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/08-releasing.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/01-/354/213/234/354/236/221/355/225/230/352/270/260.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/02-/355/224/204/353/241/234/353/260/224/354/235/264/353/215/224.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/03-/354/213/244/355/226/211-/353/260/251/354/213/235.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/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.4.5 → letify-2.4.7}/docs/guide/ko/05-/353/217/231/354/213/234-/354/213/244/355/226/211.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/06-/353/271/204/354/232/251.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/07-/353/254/270/354/240/234-/355/225/264/352/262/260.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/08-/353/246/264/353/246/254/354/212/244.md" +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/guide/ko/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/locales/README_ko.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/docs/mkdocs.yml +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/.letify.example +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/00_smoke.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/01_sweep.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/02_resume.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/03_watch.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/README_ko.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/common.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/examples/nvfp4-lora/recipe.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/cli.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/inventory.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/login.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/schema.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/secrets.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/config/writer.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/cache.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/env.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/function.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/instance.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/declare/replicas.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/errors.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/install.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/launcher.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/paths.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/codec.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/driver.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/handle.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/wire.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/protocol/worker.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/base.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/colab.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/colab_files.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/colab_keepalive.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/elice.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/kaggle_adapter.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/local.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/modal.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/modal_adapter.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/naming.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/shell.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/tunnel.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/providers/usage.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/capability.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/client.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/cuda.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/executor.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/frames.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/guard.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/tensor.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/trace.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/device/value.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/loader.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/remoting/probe.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/render.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/channel.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/lease.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/pool.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/runtime/telemetry.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/backends/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/backends/filesystem.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/backends/google_auth.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/backends/layout.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/backends/objects.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/cas.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/pathdata.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/sendorder.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/store/volume.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/stubs.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/tools.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/__init__.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/agent.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/announce.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/link.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/nat.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/pipeline.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/portmap.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/probe.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/quic.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/rendezvous.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/setup.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/sshopts.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify/transport/strategies.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/.gitignore +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/build.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-agent/Cargo.toml +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-agent/src/driver.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-agent/src/main.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/Cargo.toml +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/client.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/exports.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/image.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/lib.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/status.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-driver/src/table.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-quic/Cargo.toml +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-quic/src/main.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-wire/Cargo.toml +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-wire/src/lib.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/letify-wire/tests/throughput.rs +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-core/rust-toolchain.toml +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/.vscodeignore +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/LICENSE +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/README.md +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/media/icon.svg +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/cli.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/extension.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/history.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/model.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/poll.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/src/render.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/test/cli.test.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/test/manifest.test.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/test/model.test.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/test/regressions.test.ts +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify-ext/tsconfig.json +0 -0
- {letify-2.4.5 → letify-2.4.7}/letify_providers.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/scripts/sync-main.sh +0 -0
- {letify-2.4.5 → letify-2.4.7}/scripts/version.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/conftest.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/fake_kaggle_adapter.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/fake_modal_adapter.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_cli.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_cli_output.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_colab_keepalive.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_config.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_core.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_data.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_data_streaming.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_declare.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_device.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_distributed.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_editor_sessions.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_elice.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_frames.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_install.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_kaggle_cookie.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_link_log.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_notebook.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_parallel_placement.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_paths.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_portmap.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_protocol.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_providers.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_quic.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_remoting.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_render.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_runtime.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_ship.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_store.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_strategies.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_stubs.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_transport.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_tunnel_setup.py +0 -0
- {letify-2.4.5 → letify-2.4.7}/tests/test_version.py +0 -0
|
@@ -1508,10 +1508,15 @@ class Kaggle(Provider):
|
|
|
1508
1508
|
)
|
|
1509
1509
|
return bridge
|
|
1510
1510
|
self._links[name] = link
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1511
|
+
# Spec "Kaggle runtimes": the kernel finds libcuda through LD_LIBRARY_PATH and a
|
|
1512
|
+
# worker started over SSH does not inherit it, so it is read through the bridge and
|
|
1513
|
+
# set in the command's environment. The loader reads it when the process starts, so
|
|
1514
|
+
# setting it from inside the worker would be too late.
|
|
1515
|
+
remote = f"{self.remote_python} -u -c {_shlex.quote(BOOTSTRAP)}"
|
|
1516
|
+
libraries = self.kernel_library_path(bridge)
|
|
1517
|
+
if libraries:
|
|
1518
|
+
remote = f"env LD_LIBRARY_PATH={_shlex.quote(libraries)} {remote}"
|
|
1519
|
+
return PersistentChannel(link.ssh_command(remote), name=name)
|
|
1515
1520
|
|
|
1516
1521
|
@property
|
|
1517
1522
|
def min_session_interval_s(self) -> float:
|
|
@@ -1538,6 +1543,19 @@ class Kaggle(Provider):
|
|
|
1538
1543
|
value = self.config.option("transfer_connection_cost_s")
|
|
1539
1544
|
return max(0.0, float(value)) if isinstance(value, (int, float)) else 10.0
|
|
1540
1545
|
|
|
1546
|
+
def kernel_library_path(self, bridge: Any) -> str:
|
|
1547
|
+
"""The kernel's own ``LD_LIBRARY_PATH``, asked through the bridge, or empty.
|
|
1548
|
+
|
|
1549
|
+
Spec "Kaggle runtimes". Never raises: a session whose bridge cannot answer is one
|
|
1550
|
+
letify should still try, and the card check refuses it when CUDA cannot start.
|
|
1551
|
+
"""
|
|
1552
|
+
source = "import os\n__letify_value__ = os.environ.get('LD_LIBRARY_PATH') or ''\n"
|
|
1553
|
+
try:
|
|
1554
|
+
value, _ = bridge.request({"op": "eval", "source": source}, timeout=60)
|
|
1555
|
+
except Exception:
|
|
1556
|
+
return ""
|
|
1557
|
+
return value.strip() if isinstance(value, str) else ""
|
|
1558
|
+
|
|
1541
1559
|
def link(self, runtime: Runtime | None = None) -> Any:
|
|
1542
1560
|
"""The link carrying this runtime, or None when the bridge is carrying it.
|
|
1543
1561
|
|
|
@@ -146,9 +146,26 @@ CARD_SOURCE = (
|
|
|
146
146
|
" pass\n"
|
|
147
147
|
# /proc can list the right cards on a session whose driver cannot be used, and the
|
|
148
148
|
# first .to('cuda') then raises minutes into the call. Spec "Kaggle".
|
|
149
|
+
# cuInit is the call CUDA initialization makes, so it answers whether the driver
|
|
150
|
+
# works rather than standing in for it: a live session had the cards and the control
|
|
151
|
+
# device and still could not start CUDA. ctypes needs no torch, which the session does
|
|
152
|
+
# not have yet at this point. Spec "Kaggle".
|
|
153
|
+
# A CUresult is 0 or positive, so a sentinel of -1 would read as one. The load and
|
|
154
|
+
# the call are reported apart instead. Spec "Kaggle".
|
|
155
|
+
"__letify_libcuda = False\n"
|
|
156
|
+
"__letify_cuda = None\n"
|
|
157
|
+
"try:\n"
|
|
158
|
+
" import ctypes as _letify_ctypes\n"
|
|
159
|
+
" _letify_lib = _letify_ctypes.CDLL('libcuda.so.1')\n"
|
|
160
|
+
" __letify_libcuda = True\n"
|
|
161
|
+
" __letify_cuda = _letify_lib.cuInit(0)\n"
|
|
162
|
+
"except Exception:\n"
|
|
163
|
+
" pass\n"
|
|
149
164
|
"__letify_value__ = {\n"
|
|
150
165
|
" 'cards': __letify_cards,\n"
|
|
151
166
|
" 'driver': os.path.exists('/dev/nvidiactl'),\n"
|
|
167
|
+
" 'libcuda': __letify_libcuda,\n"
|
|
168
|
+
" 'cuinit': __letify_cuda,\n"
|
|
152
169
|
"}\n"
|
|
153
170
|
)
|
|
154
171
|
|
|
@@ -720,11 +720,15 @@ class Runtime:
|
|
|
720
720
|
except Exception:
|
|
721
721
|
# Reading /proc is not worth failing a session over when it cannot be read.
|
|
722
722
|
return
|
|
723
|
-
|
|
723
|
+
if not isinstance(found, dict):
|
|
724
|
+
found = {"cards": found, "driver": True, "libcuda": True, "cuinit": 0}
|
|
724
725
|
cards = [str(card) for card in found.get("cards") or ()]
|
|
725
726
|
driver = bool(found.get("driver"))
|
|
727
|
+
libcuda = bool(found.get("libcuda", True))
|
|
728
|
+
cuinit = found.get("cuinit")
|
|
729
|
+
started = libcuda and cuinit == 0
|
|
726
730
|
matched = [card for card in cards if fragment.casefold() in card.casefold()]
|
|
727
|
-
if len(matched) == count and len(cards) == count and driver:
|
|
731
|
+
if len(matched) == count and len(cards) == count and driver and started:
|
|
728
732
|
return
|
|
729
733
|
asked = getattr(instance, "accelerator", None) or fragment
|
|
730
734
|
arrived = ", ".join(cards) if cards else "no GPU at all"
|
|
@@ -735,6 +739,21 @@ class Runtime:
|
|
|
735
739
|
f"{arrived}, and no usable driver: /dev/nvidiactl is not there, so CUDA "
|
|
736
740
|
f"cannot start"
|
|
737
741
|
)
|
|
742
|
+
elif cards and not libcuda:
|
|
743
|
+
# Spec "Kaggle": the userspace driver library is not there at all, which is a
|
|
744
|
+
# different fault from CUDA refusing to start, and reads as a GPU session on a
|
|
745
|
+
# CPU image rather than as a CUDA error.
|
|
746
|
+
arrived = (
|
|
747
|
+
f"{arrived}, and no libcuda.so.1 to load: the cards are attached and the "
|
|
748
|
+
f"userspace driver library is missing, so nothing on this session can use "
|
|
749
|
+
f"them"
|
|
750
|
+
)
|
|
751
|
+
elif cards and not started:
|
|
752
|
+
arrived = (
|
|
753
|
+
f"{arrived}, and a driver that does not start: libcuda.so.1 loaded and "
|
|
754
|
+
f"cuInit returned {cuinit}, which is the call CUDA initialization makes, "
|
|
755
|
+
f"so nothing on this session can use those cards"
|
|
756
|
+
)
|
|
738
757
|
message = (
|
|
739
758
|
f"{self.name}: {self.provider.alias} was asked for {count} x {asked} and the "
|
|
740
759
|
f"session has {arrived}. A declaration says where a function runs, so the "
|
|
@@ -138,7 +138,7 @@ dependencies = [
|
|
|
138
138
|
|
|
139
139
|
[[package]]
|
|
140
140
|
name = "letify-agent"
|
|
141
|
-
version = "2.4.
|
|
141
|
+
version = "2.4.7"
|
|
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.
|
|
149
|
+
version = "2.4.7"
|
|
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.
|
|
167
|
+
version = "2.4.7"
|
|
168
168
|
|
|
169
169
|
[[package]]
|
|
170
170
|
name = "libc"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "letify-status",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "letify-status",
|
|
9
|
-
"version": "2.4.
|
|
9
|
+
"version": "2.4.7",
|
|
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.
|
|
5
|
+
"version": "2.4.7",
|
|
6
6
|
"publisher": "darkpyonix",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
8
8
|
"repository": {
|
|
@@ -1489,10 +1489,90 @@ def test_a_session_with_the_cards_and_a_driver_passes() -> None:
|
|
|
1489
1489
|
session.name = "letify-t4-ok"
|
|
1490
1490
|
session.provider = provider
|
|
1491
1491
|
session.instance = type("I", (), {"gpu": "T4", "accelerator": "T4"})()
|
|
1492
|
-
answer = {"cards": ["Tesla T4", "Tesla T4"], "driver": True}
|
|
1492
|
+
answer = {"cards": ["Tesla T4", "Tesla T4"], "driver": True, "cuinit": 0}
|
|
1493
1493
|
monkey = lambda self, source, timeout=None: answer # noqa: E731
|
|
1494
1494
|
Runtime.eval, original = monkey, Runtime.eval
|
|
1495
1495
|
try:
|
|
1496
1496
|
session.check_devices()
|
|
1497
1497
|
finally:
|
|
1498
1498
|
Runtime.eval = original
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
def test_a_session_whose_cuinit_fails_is_refused() -> None:
|
|
1502
|
+
"""Spec "Kaggle": cuInit is the call CUDA initialization makes.
|
|
1503
|
+
|
|
1504
|
+
A live session listed both T4s and had /dev/nvidiactl, and the first .cuda() still
|
|
1505
|
+
raised "Found no NVIDIA driver on your system" after 30 GB of shards had been sent.
|
|
1506
|
+
"""
|
|
1507
|
+
from letify.errors import InsufficientDevices
|
|
1508
|
+
from letify.runtime.session import Runtime
|
|
1509
|
+
|
|
1510
|
+
provider = kaggle_provider()
|
|
1511
|
+
session = object.__new__(Runtime)
|
|
1512
|
+
session.name = "letify-t4-fc7e27"
|
|
1513
|
+
session.provider = provider
|
|
1514
|
+
session.instance = type("I", (), {"gpu": "T4", "accelerator": "T4"})()
|
|
1515
|
+
answer = {
|
|
1516
|
+
"cards": ["Tesla T4", "Tesla T4"], "driver": True, "libcuda": True, "cuinit": 100
|
|
1517
|
+
}
|
|
1518
|
+
monkey = lambda self, source, timeout=None: answer # noqa: E731
|
|
1519
|
+
Runtime.eval, original = monkey, Runtime.eval
|
|
1520
|
+
try:
|
|
1521
|
+
with pytest.raises(InsufficientDevices) as failure:
|
|
1522
|
+
session.check_devices()
|
|
1523
|
+
finally:
|
|
1524
|
+
Runtime.eval = original
|
|
1525
|
+
message = str(failure.value)
|
|
1526
|
+
assert "cuInit" in message
|
|
1527
|
+
assert "100" in message
|
|
1528
|
+
|
|
1529
|
+
|
|
1530
|
+
def test_a_session_whose_cuinit_succeeds_passes() -> None:
|
|
1531
|
+
"""Spec: zero from cuInit is what CUDA starting looks like."""
|
|
1532
|
+
from letify.runtime.session import Runtime
|
|
1533
|
+
|
|
1534
|
+
provider = kaggle_provider()
|
|
1535
|
+
session = object.__new__(Runtime)
|
|
1536
|
+
session.name = "letify-t4-ok"
|
|
1537
|
+
session.provider = provider
|
|
1538
|
+
session.instance = type("I", (), {"gpu": "T4", "accelerator": "T4"})()
|
|
1539
|
+
answer = {
|
|
1540
|
+
"cards": ["Tesla T4", "Tesla T4"], "driver": True, "libcuda": True, "cuinit": 0
|
|
1541
|
+
}
|
|
1542
|
+
monkey = lambda self, source, timeout=None: answer # noqa: E731
|
|
1543
|
+
Runtime.eval, original = monkey, Runtime.eval
|
|
1544
|
+
try:
|
|
1545
|
+
session.check_devices()
|
|
1546
|
+
finally:
|
|
1547
|
+
Runtime.eval = original
|
|
1548
|
+
|
|
1549
|
+
|
|
1550
|
+
def test_a_missing_libcuda_is_not_reported_as_a_cuda_error() -> None:
|
|
1551
|
+
"""Spec "Kaggle": a CUresult is 0 or positive, so a sentinel would read as one.
|
|
1552
|
+
|
|
1553
|
+
A live refusal said `cuInit returned -1`, which is not a CUresult at all: it was the
|
|
1554
|
+
value for "libcuda.so.1 did not load", a different fault that reads as a GPU session
|
|
1555
|
+
on a CPU image.
|
|
1556
|
+
"""
|
|
1557
|
+
from letify.errors import InsufficientDevices
|
|
1558
|
+
from letify.runtime.session import Runtime
|
|
1559
|
+
|
|
1560
|
+
provider = kaggle_provider()
|
|
1561
|
+
session = object.__new__(Runtime)
|
|
1562
|
+
session.name = "letify-t4-d3012a"
|
|
1563
|
+
session.provider = provider
|
|
1564
|
+
session.instance = type("I", (), {"gpu": "T4", "accelerator": "T4"})()
|
|
1565
|
+
answer = {
|
|
1566
|
+
"cards": ["Tesla T4", "Tesla T4"], "driver": True, "libcuda": False, "cuinit": None
|
|
1567
|
+
}
|
|
1568
|
+
monkey = lambda self, source, timeout=None: answer # noqa: E731
|
|
1569
|
+
Runtime.eval, original = monkey, Runtime.eval
|
|
1570
|
+
try:
|
|
1571
|
+
with pytest.raises(InsufficientDevices) as failure:
|
|
1572
|
+
session.check_devices()
|
|
1573
|
+
finally:
|
|
1574
|
+
Runtime.eval = original
|
|
1575
|
+
message = str(failure.value)
|
|
1576
|
+
assert "no libcuda.so.1 to load" in message
|
|
1577
|
+
assert "cuInit returned" not in message
|
|
1578
|
+
assert "-1" not in message
|
|
@@ -317,3 +317,44 @@ def test_kaggle_does_not_offer_the_retired_p100() -> None:
|
|
|
317
317
|
message = str(failure.value)
|
|
318
318
|
assert "2026-09-15" in message
|
|
319
319
|
assert "T4" in message
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_the_worker_carries_the_kernel_s_library_path(monkeypatch) -> None:
|
|
323
|
+
"""Spec "Kaggle runtimes": the loader reads LD_LIBRARY_PATH when the process starts.
|
|
324
|
+
|
|
325
|
+
A GPU session has its cards, its /dev/nvidia* nodes and a libcuda.so.1 at
|
|
326
|
+
/usr/local/nvidia/lib64 that ldconfig does not list. The kernel finds it through that
|
|
327
|
+
variable; a worker started over SSH does not inherit it, which the kernel bridge never
|
|
328
|
+
suffered and the punched link introduced.
|
|
329
|
+
"""
|
|
330
|
+
built: list[list[str]] = []
|
|
331
|
+
provider = kaggle()
|
|
332
|
+
|
|
333
|
+
class Bridge:
|
|
334
|
+
def request(self, message, timeout=None):
|
|
335
|
+
source = message.get("source", "")
|
|
336
|
+
if "LD_LIBRARY_PATH" in source:
|
|
337
|
+
return "/usr/local/nvidia/lib64:/usr/local/cuda/lib64", None
|
|
338
|
+
return "", None
|
|
339
|
+
|
|
340
|
+
class Link:
|
|
341
|
+
strategy = "tcp_punch"
|
|
342
|
+
rtt_ms = 120.0
|
|
343
|
+
|
|
344
|
+
def ssh_command(self, remote_command=None):
|
|
345
|
+
return ["ssh", "-p", "40000", "root@127.0.0.1", remote_command or ""]
|
|
346
|
+
|
|
347
|
+
class Fake:
|
|
348
|
+
def __init__(self, command, name=None, **kwargs):
|
|
349
|
+
built.append(command)
|
|
350
|
+
self.name = name
|
|
351
|
+
|
|
352
|
+
monkeypatch.setattr(provider, "link_over", lambda channel, name=None: Link())
|
|
353
|
+
monkeypatch.setattr("letify.runtime.channel.PersistentChannel", Fake)
|
|
354
|
+
provider.channel_over(Bridge(), name="k-1")
|
|
355
|
+
|
|
356
|
+
remote = built[0][-1]
|
|
357
|
+
assert "LD_LIBRARY_PATH=" in remote
|
|
358
|
+
assert "/usr/local/nvidia/lib64" in remote
|
|
359
|
+
# Before the interpreter, or the loader has already read the environment.
|
|
360
|
+
assert remote.index("LD_LIBRARY_PATH=") < remote.index("python3")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
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.4.5 → letify-2.4.7}/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.4.5 → letify-2.4.7}/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.4.5 → letify-2.4.7}/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.4.5 → letify-2.4.7}/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
|