Skill Tracer
Skills › Scientific Agent Skills › pufferlib › training.md

training.md

329 lines · skills/pufferlib/references/training.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# Training, Evaluation, Configuration, and Logging
2
3Review: **2026-10-01**. Native 5.0 guidance is in [native-5.md](native-5.md);
43.0 and the pinned 4.0 snapshot below are historical profiles.
5

Training, Evaluation, Configuration, and Logging

Review: 2026-10-01. Native 5.0 guidance is in native-5.md; 3.0 and the pinned 4.0 snapshot below are historical profiles.

Raw lines 1 to 5
6## Choose a version profile first
7

Choose a version profile first

Raw lines 6 to 7
8### Published stable package
9
10PyPI's latest stable `pufferlib` release is **3.0.0**, published
11**2025-06-23**. It declares Python `>=3.9` and is distributed only as a
1260.7 MB source archive:
14```text
15pufferlib-3.0.0.tar.gz
16sha256: 7df3a3e3f5f894d78d2a1f5374097890aec01473183e748abefe4f3faa10eaa9
17```
19The uploaded metadata depends on NumPy `<2.0`, Gym `<=0.23`, Gymnasium
20`<=0.29.1`, PettingZoo `<=1.24.1`, Shimmy, Torch, Neptune, W&B, and other
21packages without a complete transitive lock. It does not declare a CUDA
22version or a minimum Torch version. Do not invent compatibility guarantees.

Published stable package

PyPI's latest stable pufferlib release is 3.0.0, published 2025-06-23. It declares Python >=3.9 and is distributed only as a 60.7 MB source archive:

pufferlib-3.0.0.tar.gz
sha256: 7df3a3e3f5f894d78d2a1f5374097890aec01473183e748abefe4f3faa10eaa9

The uploaded metadata depends on NumPy <2.0, Gym <=0.23, Gymnasium <=0.29.1, PettingZoo <=1.24.1, Shimmy, Torch, Neptune, W&B, and other packages without a complete transitive lock. It does not declare a CUDA version or a minimum Torch version. Do not invent compatibility guarantees.

Raw lines 8 to 23
24### Reviewed 4.0 source line
26The historical branch is `4.0`; its `pyproject.toml` says version `4.0.0`,
27Python `>=3.10`, and Torch `>=2.9`. As of the research date, this source line
28is not the latest stable PyPI artifact.
30The historical PufferTank 4.0 Dockerfile uses:
32- Ubuntu 24.04
33- NVIDIA CUDA `13.0.2` cuDNN development image
34- Python 3.12
35- the CUDA 13.0 PyTorch wheel index
36- Nsight Systems `2025.6.3`
38The Dockerfile does **not** pin an exact Torch wheel, uv version, PufferLib
39commit, or every apt package. It is an upstream convenience environment, not a
40complete reproducibility lock.

Reviewed 4.0 source line

The historical branch is 4.0; its pyproject.toml says version 4.0.0, Python >=3.10, and Torch >=2.9. As of the research date, this source line is not the latest stable PyPI artifact.

The historical PufferTank 4.0 Dockerfile uses:

  • Ubuntu 24.04
  • NVIDIA CUDA 13.0.2 cuDNN development image
  • Python 3.12
  • the CUDA 13.0 PyTorch wheel index
  • Nsight Systems 2025.6.3

The Dockerfile does not pin an exact Torch wheel, uv version, PufferLib commit, or every apt package. It is an upstream convenience environment, not a complete reproducibility lock.

Raw lines 24 to 41
42## Reproducible uv workflow
44Installation/native training commands in this reference are illustrative and
45were not executed in this review. Source signatures below were checked against
46the hash-verified PyPI sdist, which differs from branch `3.0`.
48Do not use an unpinned `uv pip install pufferlib`. Work in a disposable,
49project-specific environment and commit `pyproject.toml` plus `uv.lock`.
51For the published profile, after reviewing the source archive and build:
53```bash
54uv venv --python 3.11
55uv add --exact --no-sync "pufferlib==3.0.0"
56uv lock
57uv sync --frozen
58```
60Confirm the lock records the published SHA-256 above and review every resolved
61dependency. The 3.0.0 source build can compile native code and may fetch build
62assets. Resolve and build in a sandbox with no credentials or sensitive mounts.
63Do not treat a successful resolver run as a security review.
65For 4.0 source work, pin an immutable revision rather than branch `4.0`:
67```bash
68uv add --no-sync \
69 "pufferlib @ git+https://github.com/PufferAI/PufferLib.git@25647630e1b15330bb3153a5a0d3ff8d234c3acf"
70uv lock
71```
73The commit above is the reviewed 4.0 branch head on 2026-07-23. Re-review before
74updating it. Native training still requires an audited build of a specific
75environment; uv locking does not lock compilers, CUDA, NCCL, cuDNN, Raylib, or
76system libraries.
78Never run remote install scripts directly from a pipe. Download, inspect, pin,
79verify, and execute only in an appropriate sandbox.

Reproducible uv workflow

Installation/native training commands in this reference are illustrative and were not executed in this review. Source signatures below were checked against the hash-verified PyPI sdist, which differs from branch 3.0.

Do not use an unpinned uv pip install pufferlib. Work in a disposable, project-specific environment and commit pyproject.toml plus uv.lock.

For the published profile, after reviewing the source archive and build:

uv venv --python 3.11
uv add --exact --no-sync "pufferlib==3.0.0"
uv lock
uv sync --frozen

Confirm the lock records the published SHA-256 above and review every resolved dependency. The 3.0.0 source build can compile native code and may fetch build assets. Resolve and build in a sandbox with no credentials or sensitive mounts. Do not treat a successful resolver run as a security review.

For 4.0 source work, pin an immutable revision rather than branch 4.0:

uv add --no-sync \
  "pufferlib @ git+https://github.com/PufferAI/PufferLib.git@25647630e1b15330bb3153a5a0d3ff8d234c3acf"
uv lock

The commit above is the reviewed 4.0 branch head on 2026-07-23. Re-review before updating it. Native training still requires an audited build of a specific environment; uv locking does not lock compilers, CUDA, NCCL, cuDNN, Raylib, or system libraries.

Never run remote install scripts directly from a pipe. Download, inspect, pin, verify, and execute only in an appropriate sandbox.

Raw lines 42 to 80
81## Published 3.0.0 training

Published 3.0.0 training

Raw lines 81 to 82
83### CLI
85The 3.0 console entry point is `puffer = pufferlib.pufferl:main`:
87```bash
88puffer train ENV_NAME [OPTIONS]
89puffer eval ENV_NAME [OPTIONS]
90puffer sweep ENV_NAME [OPTIONS]
91puffer autotune ENV_NAME [OPTIONS]
92puffer profile ENV_NAME [OPTIONS]
93puffer export ENV_NAME [OPTIONS]
94```
96Environment, vector, policy, recurrent, training, and sweep values come from
97INI sections. Overrides use section-qualified flags:
99```bash
100puffer train puffer_breakout \
101 --train.device cpu \
102 --train.total-timesteps 100000 \
103 --vec.backend Serial \
104 --vec.num-envs 2
105```
107Run `puffer train ENV_NAME --help` against the exact locked environment because
108available options are generated from merged INI files.

CLI

The 3.0 console entry point is puffer = pufferlib.pufferl:main:

puffer train ENV_NAME [OPTIONS]
puffer eval ENV_NAME [OPTIONS]
puffer sweep ENV_NAME [OPTIONS]
puffer autotune ENV_NAME [OPTIONS]
puffer profile ENV_NAME [OPTIONS]
puffer export ENV_NAME [OPTIONS]

Environment, vector, policy, recurrent, training, and sweep values come from INI sections. Overrides use section-qualified flags:

puffer train puffer_breakout \
  --train.device cpu \
  --train.total-timesteps 100000 \
  --vec.backend Serial \
  --vec.num-envs 2

Run puffer train ENV_NAME --help against the exact locked environment because available options are generated from merged INI files.

Raw lines 83 to 109
110### Python API
112The stable trainer is `pufferlib.pufferl.PuffeRL`, not a top-level
113`pufferlib.PuffeRL`:
115```python
116from pufferlib import pufferl
118args = pufferl.load_config("puffer_breakout")
119vecenv = pufferl.load_env("puffer_breakout", args)
120policy = pufferl.load_policy(args, vecenv)
121train_config = dict(args["train"], env="puffer_breakout")
122trainer = pufferl.PuffeRL(train_config, vecenv, policy)
124try:
125 while trainer.epoch < trainer.total_epochs:
126 trainer.evaluate()
127 trainer.train()
128finally:
129 trainer.close()
130```
132The exact public methods include `evaluate`, `train`, `mean_and_log`,
133`save_checkpoint`, `print_dashboard`, and `close`. Use the CLI when possible;
134the Python trainer is a relatively low-level implementation surface.

Python API

The stable trainer is pufferlib.pufferl.PuffeRL, not a top-level pufferlib.PuffeRL:

from pufferlib import pufferl

args = pufferl.load_config("puffer_breakout")
vecenv = pufferl.load_env("puffer_breakout", args)
policy = pufferl.load_policy(args, vecenv)
train_config = dict(args["train"], env="puffer_breakout")
trainer = pufferl.PuffeRL(train_config, vecenv, policy)

try:
    while trainer.epoch < trainer.total_epochs:
        trainer.evaluate()
        trainer.train()
finally:
    trainer.close()

The exact public methods include evaluate, train, mean_and_log, save_checkpoint, print_dashboard, and close. Use the CLI when possible; the Python trainer is a relatively low-level implementation surface.

Raw lines 110 to 135
136### Stable configuration checks
138- Make rollout/batch relationships explicit; do not rely on `auto` in a
139 published experiment.
140- Record environment, vector, policy, recurrent, and train sections verbatim.
141- Seed Python, NumPy and Torch **before constructing the policy**, in addition
142 to environment/action-space seeds. The sdist trainer reads `[train] seed` for
143 vector reset but leaves Python/NumPy/Torch seeding commented out. A config seed
144 alone does not seed initial weights.
145- Record `torch_deterministic`, precision, compile settings, optimizer, horizon,
146 minibatch, and total timesteps.
147- Keep evaluation seeds, instances, and metrics separate from training.

Stable configuration checks

  • Make rollout/batch relationships explicit; do not rely on auto in a published experiment.
  • Record environment, vector, policy, recurrent, and train sections verbatim.
  • Seed Python, NumPy and Torch before constructing the policy, in addition to environment/action-space seeds. The sdist trainer reads [train] seed for vector reset but leaves Python/NumPy/Torch seeding commented out. A config seed alone does not seed initial weights.
  • Record torch_deterministic, precision, compile settings, optimizer, horizon, minibatch, and total timesteps.
  • Keep evaluation seeds, instances, and metrics separate from training.
Raw lines 136 to 148
149### Important 3.0 learner limitations
151The sdist allocates a truncation tensor but does not populate it in rollouts;
152it leaves a TODO for separate truncation handling and explicitly notes that
153masks are not yet handled in the learner. Preserving adapter flags does not
154prove correct time-limit targets or exclusion of inactive PettingZoo slots.
155Audit/fix the learner and run a short analytic terminal/truncation/mask test
156before using those cases for scientific conclusions. `evaluate()` collects
157training rollouts; it is not held-out evaluation. The stock `puffer eval` path
158is a rendering loop, not a bounded multi-episode statistical evaluator.
160The planner's `horizon` maps to `--train.bptt-horizon` in 3.0 and
161`--train.horizon` in 4.0. Its preview leaves environment-specific rollout size,
162zero-copy/start-method selection and native build/device selection to the
163caller. In 3.0, record `batch_size` explicitly, ensure it is at least the
164minibatch and provides enough horizon segments for every agent. CLI booleans
165in the historical parsers use Python `bool(string)`: `False` is truthy. Set
166false-valued booleans in INI or the Python argument mapping, not textual CLI
167`False`, then inspect the resolved config.

Important 3.0 learner limitations

The sdist allocates a truncation tensor but does not populate it in rollouts; it leaves a TODO for separate truncation handling and explicitly notes that masks are not yet handled in the learner. Preserving adapter flags does not prove correct time-limit targets or exclusion of inactive PettingZoo slots. Audit/fix the learner and run a short analytic terminal/truncation/mask test before using those cases for scientific conclusions. evaluate() collects training rollouts; it is not held-out evaluation. The stock puffer eval path is a rendering loop, not a bounded multi-episode statistical evaluator.

The planner's horizon maps to --train.bptt-horizon in 3.0 and --train.horizon in 4.0. Its preview leaves environment-specific rollout size, zero-copy/start-method selection and native build/device selection to the caller. In 3.0, record batch_size explicitly, ensure it is at least the minibatch and provides enough horizon segments for every agent. CLI booleans in the historical parsers use Python bool(string): False is truthy. Set false-valued booleans in INI or the Python argument mapping, not textual CLI False, then inspect the resolved config.

Raw lines 149 to 168
169## Historical 4.0 training
171Build one audited environment, then use:
173```bash
174puffer train breakout
175puffer eval breakout --load-model-path checkpoints/.../weights.bin
176puffer sweep breakout
177puffer match breakout \
178 --load-model-path trusted-a.bin \
179 --load-enemy-model-path trusted-b.bin
180```
182Historical modes are `train`, `eval`, `sweep`, `paretosweep`, and `match`.
183Native training is the default. `--slowly` selects the Torch fallback.
184Configuration uses sections such as:
186```ini
187[vec]
188total_agents = 4096
189num_buffers = 2
190num_threads = 16
192[train]
193total_timesteps = 10_000_000
194minibatch_size = 8192
195horizon = 64
197[torch]
198network = MinGRU
199encoder = DefaultEncoder
200decoder = DefaultDecoder
201```
203Reviewed 4.0 source validates that `minibatch_size` is divisible by `horizon` and
204does not exceed `horizon * total_agents`. Multi-GPU launch uses spawn. Do a
205small CPU/local build and contract test before CUDA training.

Historical 4.0 training

Build one audited environment, then use:

puffer train breakout
puffer eval breakout --load-model-path checkpoints/.../weights.bin
puffer sweep breakout
puffer match breakout \
  --load-model-path trusted-a.bin \
  --load-enemy-model-path trusted-b.bin

Historical modes are train, eval, sweep, paretosweep, and match. Native training is the default. --slowly selects the Torch fallback. Configuration uses sections such as:

[vec]
total_agents = 4096
num_buffers = 2
num_threads = 16

[train]
total_timesteps = 10_000_000
minibatch_size = 8192
horizon = 64

[torch]
network = MinGRU
encoder = DefaultEncoder
decoder = DefaultDecoder

Reviewed 4.0 source validates that minibatch_size is divisible by horizon and does not exceed horizon * total_agents. Multi-GPU launch uses spawn. Do a small CPU/local build and contract test before CUDA training.

Raw lines 169 to 206
Starts an agent spawn :204
207## Held-out evaluation
209Training rollouts are not evaluation. For every reported result:
2111. Freeze one checkpoint-selection rule before inspecting held-out scores.
2122. Construct fresh evaluation environment instances.
2133. Use evaluation seeds disjoint from training seeds.
2144. Disable optimizer updates, exploration noise unless explicitly measuring it,
215 curriculum updates, normalization-stat updates, and reward shaping used only
216 for training.
2175. Report deterministic and stochastic policy protocols separately.
2186. Run enough episodes for uncertainty; report per-seed results and aggregate
219 intervals, not only a best run.
2207. Preserve terminated versus truncated semantics in return/length accounting.
2218. Record wrappers, frame skip, autoreset mode, opponent pool, policy state
222 reset, and rendering state.
224Generate a starting plan:
226```bash
227python3 scripts/repro_plan.py --environment synthetic
228```

Held-out evaluation

Training rollouts are not evaluation. For every reported result:

  1. Freeze one checkpoint-selection rule before inspecting held-out scores.
  2. Construct fresh evaluation environment instances.
  3. Use evaluation seeds disjoint from training seeds.
  4. Disable optimizer updates, exploration noise unless explicitly measuring it, curriculum updates, normalization-stat updates, and reward shaping used only for training.
  5. Report deterministic and stochastic policy protocols separately.
  6. Run enough episodes for uncertainty; report per-seed results and aggregate intervals, not only a best run.
  7. Preserve terminated versus truncated semantics in return/length accounting.
  8. Record wrappers, frame skip, autoreset mode, opponent pool, policy state reset, and rendering state.

Generate a starting plan:

python3 scripts/repro_plan.py --environment synthetic
Raw lines 207 to 229
Runs code python3 scripts/repro_plan.py :227
230## Checkpoints
232PufferLib 3.0 saves a policy `state_dict` with `torch.save` and a separate
233trainer state containing optimizer state, global step, epoch, and run ID. Its
234loading paths call `torch.load`. The 4.0 native backend writes `.bin` weight
235files; the 4.0 Torch fallback also uses `torch.save`/`torch.load`.
237Rules:
239- Never load an untrusted checkpoint, even to “inspect” it.
240- Record SHA-256, size, source URL, immutable revision, license, environment,
241 policy architecture, package lock, and training config in a strict JSON
242 sidecar.
243- Do not use `latest` in a reproducible run; resolve and record the exact path
244 and digest.
245- Do not auto-download a run artifact by ID.
246- Test restore and evaluation in a disposable environment before a long resume.
247- A model-only checkpoint is not a bitwise resume; optimizer, scheduler,
248 normalizer, RNG, environment, and recurrent state may also matter.
250Safe metadata inspection:
252```bash
253python3 scripts/inspect_checkpoint.py trusted/model.pt \
254 --expected-sha256 EXPECTED_DIGEST
255```
257The helper hashes and classifies bytes only. It never imports Torch, invokes
258pickle, opens archive members, or extracts files.

Checkpoints

PufferLib 3.0 saves a policy state_dict with torch.save and a separate trainer state containing optimizer state, global step, epoch, and run ID. Its loading paths call torch.load. The 4.0 native backend writes .bin weight files; the 4.0 Torch fallback also uses torch.save/torch.load.

Rules:

  • Never load an untrusted checkpoint, even to “inspect” it.
  • Record SHA-256, size, source URL, immutable revision, license, environment, policy architecture, package lock, and training config in a strict JSON sidecar.
  • Do not use latest in a reproducible run; resolve and record the exact path and digest.
  • Do not auto-download a run artifact by ID.
  • Test restore and evaluation in a disposable environment before a long resume.
  • A model-only checkpoint is not a bitwise resume; optimizer, scheduler, normalizer, RNG, environment, and recurrent state may also matter.

Safe metadata inspection:

python3 scripts/inspect_checkpoint.py trusted/model.pt \
  --expected-sha256 EXPECTED_DIGEST

The helper hashes and classifies bytes only. It never imports Torch, invokes pickle, opens archive members, or extracts files.

Raw lines 230 to 259
Runs code python3 scripts/inspect_checkpoint.py :253
260## External logging
262Local logging is the default. W&B is an optional network service
263that may transmit configuration, metrics, source metadata, hardware telemetry,
264stdout/stderr, and explicitly uploaded checkpoints/artifacts. They can create
265storage, seat, compute, or retention costs and are subject to vendor privacy,
266access, and retention policies.
268Credential rules:
270- W&B: use the named environment variable `WANDB_API_KEY` or an approved secret
271 manager.
272- Neptune historically used `NEPTUNE_API_TOKEN`; its hosted service shut down
273 on 2026-03-05. The planner rejects Neptune for both profiles.
274- Never pass either secret as a CLI argument, INI/JSON value, logger config,
275 tag, run name, or chat/tool input.
276- Never print the value or include it in a broad environment dump.
277- Do not recursively search for `.env` files. If policy permits a local secret
278 file, read only the explicitly named key from the explicitly named file.
279- Sanitize configuration before logging; reject keys containing token, secret,
280 password, credential, authorization, private key, or API key.
281- Disable checkpoint/source upload unless separately approved.
283The 3.0 source contains both logger flags; only W&B remains a candidate for a
284new hosted run. Its SDK handles authentication, requests, uploads, retries and
285pagination; this skill implements no REST endpoint or manual HTTP payload.
286W&B's official SDK reads `WANDB_API_KEY` and uses `https://api.wandb.ai` by default
287(or a reviewed deployment selected by `WANDB_BASE_URL`). No authenticated W&B
288run was performed in this review; SDK source inspection is not live-service
289verification.
291The **actual PyPI 3.0.0 sdist** calls `log_artifact` unconditionally at W&B close;
292there is no `--no-model-upload` option. The pinned 4.0 ordinary W&B training path
293also uploads its model. Use local logging, or approve model upload explicitly;
294metrics-only use needs a reviewed custom logger/patch. The planner rejects a
295W&B plan that omits this artifact opt-in:
297```bash
298python3 scripts/train_template.py \
299 --logger wandb \
300 --enable-external-logging \
301 --acknowledge-external-disclosure \
302 --upload-checkpoints
303```
305The helper remains a dry-run and never reads credentials or contacts the service.

External logging

Local logging is the default. W&B is an optional network service that may transmit configuration, metrics, source metadata, hardware telemetry, stdout/stderr, and explicitly uploaded checkpoints/artifacts. They can create storage, seat, compute, or retention costs and are subject to vendor privacy, access, and retention policies.

Credential rules:

  • W&B: use the named environment variable WANDB_API_KEY or an approved secret manager.
  • Neptune historically used NEPTUNE_API_TOKEN; its hosted service shut down on 2026-03-05. The planner rejects Neptune for both profiles.
  • Never pass either secret as a CLI argument, INI/JSON value, logger config, tag, run name, or chat/tool input.
  • Never print the value or include it in a broad environment dump.
  • Do not recursively search for .env files. If policy permits a local secret file, read only the explicitly named key from the explicitly named file.
  • Sanitize configuration before logging; reject keys containing token, secret, password, credential, authorization, private key, or API key.
  • Disable checkpoint/source upload unless separately approved.

The 3.0 source contains both logger flags; only W&B remains a candidate for a new hosted run. Its SDK handles authentication, requests, uploads, retries and pagination; this skill implements no REST endpoint or manual HTTP payload. W&B's official SDK reads WANDB_API_KEY and uses https://api.wandb.ai by default (or a reviewed deployment selected by WANDB_BASE_URL). No authenticated W&B run was performed in this review; SDK source inspection is not live-service verification.

The actual PyPI 3.0.0 sdist calls log_artifact unconditionally at W&B close; there is no --no-model-upload option. The pinned 4.0 ordinary W&B training path also uploads its model. Use local logging, or approve model upload explicitly; metrics-only use needs a reviewed custom logger/patch. The planner rejects a W&B plan that omits this artifact opt-in:

python3 scripts/train_template.py \
  --logger wandb \
  --enable-external-logging \
  --acknowledge-external-disclosure \
  --upload-checkpoints

The helper remains a dry-run and never reads credentials or contacts the service.

Raw lines 260 to 306
Runs code python3 scripts/train_template.py :298
307## Sources
309- [PyPI: pufferlib 3.0.0](https://pypi.org/project/pufferlib/3.0.0/) —
310 released 2025-06-23; accessed 2026-07-23.
311- [PyPI 3.0.0 JSON metadata](https://pypi.org/pypi/pufferlib/3.0.0/json) —
312 package requirements and digest; accessed 2026-07-23.
313- [PufferLib 3.0 trainer](https://github.com/PufferAI/PufferLib/blob/3.0/pufferlib/pufferl.py)
314 — moving branch; API claims above use the [exact 3.0.0 sdist](https://files.pythonhosted.org/packages/7c/e1/5292f9b69c6263707b40ba04a87e6b9bcc177281d31092f77afd90c412f1/pufferlib-3.0.0.tar.gz), verified 2026-10-01.
315- [PufferLib 3.0 default config](https://github.com/PufferAI/PufferLib/blob/3.0/pufferlib/config/default.ini)
316 — stable parameters; accessed 2026-07-23.
317- [PufferLib live 5.0 docs](https://puffer.ai/docs.html) — current native workflow;
318 rechecked 2026-10-01. Historical 4.0 claims use its pinned source below.
319- [PufferLib 4.0 trainer](https://github.com/PufferAI/PufferLib/blob/25647630e1b15330bb3153a5a0d3ff8d234c3acf/pufferlib/pufferl.py)
320 — historical modes/config/checkpoints; accessed 2026-07-23.
321- [PufferLib 4.0 package metadata](https://github.com/PufferAI/PufferLib/blob/25647630e1b15330bb3153a5a0d3ff8d234c3acf/pyproject.toml)
322 — historical Python/Torch requirements; accessed 2026-07-23.
323- [PufferTank 4.0 Dockerfile](https://github.com/PufferAI/PufferTank/blob/4.0/puffertank.dockerfile)
324 — CUDA/Python reference environment; accessed 2026-07-23.
325- [Neptune shutdown notice](https://docs.neptune.ai/) — permanent 2026-03-05
326 shutdown; checked 2026-10-01.
327- [W&B official login source](https://github.com/wandb/wandb/blob/main/wandb/sdk/wandb_login.py)
328 and [environment variable names](https://github.com/wandb/wandb/blob/main/wandb/env.py)
329 — checked 2026-10-01; hosted docs redirected during review.

Sources

Raw lines 307 to 329