File size: 6,794 Bytes
a4df021
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
454413e
a4df021
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
> RECOVERED 2026-05-23T23:57:12 from Claude session transcripts (last Write; later Edit-tool revisions not applied). Original file was missing from disk.

# Upload Guidelines

**Status:** STUB (to be expanded after EVAL_GUIDELINES.md is locked through 31B Q4_K_M + Q6_K + dual-eval wrapper). Holding scaffolding so cross-references in companion docs resolve to a real path.

**Scope:** how to upload artifacts (adapters, merged safetensors, GGUF quants, datasets, model cards) to Hugging Face reliably and in the right order.

**Companion docs:**
- [EVAL_GUIDELINES.md](EVAL_GUIDELINES.md) β€” how to evaluate models after uploading.
- [INSTRUCT_MASKING_TECHNIQUES.md](INSTRUCT_MASKING_TECHNIQUES.md) β€” masking/merging techniques that produce the artifacts.
- [MERGE_MADNESS.md](MERGE_MADNESS.md) β€” community-model merge recipe cookbook.
- This doc β€” how to *upload* the outputs of the above.

---

## 0. Hard rules (always apply)

**0.1 `HF_HUB_DISABLE_XET=1` is mandatory for every upload.** Without it, Xet de-dup throttled our 31B uploads to ~1 MB/s (2026-05-22/23). With it, sustained ~6 MB/s on the same files. Set the env var inline on every Python invocation:

```bash
HF_HUB_DISABLE_XET=1 python upload_script.py
```

**0.2 `chat_template.jinja` must be present in the source dir before quant.** Otherwise the GGUF ships templateless β†’ llama-server guesses ChatML β†’ leak + IFEval drop. (Top-level rule, memory `feedback_copy_jinja_before_quant.md`. Lived through this on 2026-05-23.)

**0.3 GGUF provider for community quants:** bartowski only. **NEVER unsloth.** (Memory `feedback_gguf_quants.md`.) AesSedai / ddh0 are also OK for MoE specifically.

**0.4 File-size verification before declaring "done".** Especially with xet stalls or transient HF errors: after upload, fetch the file's HF-side size via `HfApi.model_info(repo).siblings` and compare against the local size. Don't trust the absence of a Python exception alone. (Memory `project_b200_runpod_orchestration.md`.)

**0.5 Adapter-first upload order for multi-artifact releases.** Adapter (~hundreds of MB) β†’ merged safetensors (~hundreds of GB raw / ~30-120 GB bf16) β†’ GGUF quants. So downstream users can grab the cheap thing while the heavy artifacts are still uploading.

---

## 1. Token storage and which account uploads where

**1.1 Local token file.** The default Hugging Face CLI token lives at `/home/aibox/.cache/huggingface/token` on this box (single line, starts with `hf_…`). `huggingface_hub.HfApi()` and the `huggingface-cli` both pick this up automatically β€” **scripts should not read it explicitly or print it**.

**1.2 Verify which account a token belongs to** *without printing the token itself*:

```bash
python -c "from huggingface_hub import whoami; print(whoami())"
```

This prints `{'name': '<username>', ...}` plus the `orgs` list the token can act under.

**1.3 ToastyPigeon vs rpDungeon.** The default token is currently the ToastyPigeon account token. Whether it has org-membership / write access to `rpDungeon/*` repos depends on org settings on the HF side:
- If it does: same token writes to both. Verify with `whoami()`'s `orgs` field.
- If it does not: we need a separate token for rpDungeon. Store as a second file (suggested path: `/home/aibox/.cache/huggingface/token_rpdungeon`) and select via the `token=` kwarg in `HfApi(token=open(path).read().strip())` rather than mutating the global file.

**Action item (PENDING):** confirm whether the current default token has write access to `rpDungeon/*`. The gembrain_equinox upload at 21:07 UTC succeeded against `rpDungeon/Gemma4-31b-Gembrain-Equinox`, so at least at that moment it did. If a future upload to rpDungeon returns 403, swap the token via the second-file pattern.

**1.4 Never paste tokens into committed files, scripts, Discord, or this doc.** Read from `/home/aibox/.cache/huggingface/token` or the named-second-file path. Anything printed in CI/logs must be masked (`hf_…` style).

**1.5 Rotation.** If a token is ever exposed (Discord paste, accidental commit, leaked log), rotate immediately at https://huggingface.co/settings/tokens. Update the local file. No emergency rebuild of repos is needed β€” only the token changes.

---

## 2. Upload patterns (TO BE FILLED β€” placeholders for now)

### 2.1 Single file (GGUF / safetensors shard / adapter zip)
```python
import os
os.environ.setdefault("HF_HUB_DISABLE_XET", "1")
from huggingface_hub import HfApi
HfApi().upload_file(
    path_or_fileobj=local_path,
    path_in_repo=remote_name,
    repo_id=repo,
    repo_type="model",
)
```

### 2.2 Whole directory (adapter folder, merged dir)
- Use `HfApi.upload_folder` with `allow_patterns` / `ignore_patterns`.
- Always ignore checkpoint dirs / optimizer states unless explicitly republishing them.

### 2.3 Template-only re-mint of an existing GGUF on HF
- `gguf_new_metadata.py --chat-template-file <jinja>` on the local GGUF (~5 min).
- Swap-and-upload over the existing HF copy (~50 min at 6 MB/s for 18.7 GB Q4_K_M).
- Reference script: `instruct_mask/refresh_gguf_chat_templates.sh` + `upload_tplmod_ggufs.sh`.

### 2.4 Large datasets
- *(to fill β€” covers shard-size choice, `repo_type='dataset'`, parquet vs jsonl)*

---

## 3. Detached-job harness for uploads

Every upload that's expected to take longer than ~5 min should run via `run-detached` (systemd-user) so it survives Claude restarts, hooks into the OOM watcher, and has a memory cap. Pure-network uploads can use `RUN_DETACHED_ALLOW_SMALL_CAP=1 run-detached <name> 8G ...` (network-only, no model load).

Reference: `discord-project/instruct_mask/upload_tplmod_ggufs.sh` + its `run-detached` launch line.

---

## 4. Validation checklist (after every upload)

- [ ] `HfApi.model_info(repo)` returns successfully and lists the file with the expected size
- [ ] If GGUF: file is downloadable + llama-server boots against it cleanly (smoke ping)
- [ ] If adapter: PeftModel.from_pretrained loads it cleanly against the declared base
- [ ] Repo README updated if filename / content changed
- [ ] Discord ping with the HF URL (so the next eval / merge step has the source)

---

## 5. Open questions (to scope when expanding this doc)

- Best multipart chunk size for 25-50 GB files via `HfApi.upload_file`
- Resume semantics: if upload script dies at 80%, can we resume vs starting over?
- How to handle quota / repo-size limits β€” staging on a different repo first?
- Concurrent upload throttling β€” does running 2 uploads in parallel double or halve throughput at our 6 MB/s ceiling?
- Whether to set `commit_message` per file (good audit trail) vs leaving default

---

## 6. Changelog

- 2026-05-23 β€” Stub created. Cross-referenced from MASK / MERGE / EVAL docs. Awaiting full content pass after EVAL_GUIDELINES.md lands.