Cion-lab commited on
Commit
15c6b87
·
verified ·
1 Parent(s): a2bfa41

hubsync.ensure_repo must install creds first (real cause of the 401); correct E-017 diagnosis

Browse files
Files changed (1) hide show
  1. build/hubsync.py +113 -105
build/hubsync.py CHANGED
@@ -1,105 +1,113 @@
1
- # Hub sync for the mix build: the instance is scratch, so nothing that took an hour to produce may live
2
- # only there (§3.13 "never let the instance be the only copy", applied to a CPU job rather than a
3
- # checkpoint).
4
- #
5
- # Granularity is one *source*: staging a source takes 5-40 minutes, so publishing its shards the moment
6
- # it finishes bounds the loss from any interruption to the one source in flight, and a resume can skip
7
- # every source already on the Hub without re-reading it.
8
- #
9
- # The stage repo is private-in-practice but public by project rule; it holds no credentials and no
10
- # benchmark material -- only token ids from the approved mix.
11
-
12
- import json
13
- import os
14
-
15
-
16
- def repo_id_from_env(default=None):
17
- return os.environ.get("OUNCE100M_STAGE_REPO") or default
18
-
19
-
20
- def api():
21
- import os
22
-
23
- import ounce100m_credentials
24
-
25
- ounce100m_credentials.install()
26
- from huggingface_hub import HfApi
27
-
28
- return HfApi(token=os.environ["HF_TOKEN"])
29
-
30
-
31
- def ensure_repo(repo):
32
- from huggingface_hub import create_repo
33
-
34
- # hub 1.11 (the image's version) takes the owner inside repo_id and has no `namespace` kwarg (E-013)
35
- create_repo(repo_id=repo, repo_type="dataset", private=False, exist_ok=True)
36
- return repo
37
-
38
-
39
- def remote_stage_files(repo):
40
- """Set of relative paths under stage/ that already exist on the Hub."""
41
- import urllib.request
42
-
43
- url = f"https://huggingface.co/api/datasets/{repo}/tree/main/stage?recursive=true"
44
- try:
45
- with urllib.request.urlopen(url, timeout=90) as r:
46
- entries = json.loads(r.read().decode())
47
- except Exception:
48
- return set()
49
- out = set()
50
- for e in entries:
51
- if e.get("type") != "file":
52
- continue
53
- out.add(e["path"])
54
- # a directory in the tree listing means we must recurse; recursive=true should cover it, but
55
- # if the server ignored the flag we get dirs and no files, which the caller detects as empty
56
- return out
57
-
58
-
59
- def upload_source(root, key, repo):
60
- """Publish stage/<key>/* in one commit. Returns the file names pushed."""
61
- a = api()
62
- local = os.path.join(root, "stage", key)
63
- files = sorted(f for f in os.listdir(local) if f.endswith((".bin", ".json")))
64
- if not files:
65
- return []
66
- a.upload_folder(folder_path=local, path_in_repo=f"stage/{key}", repo_id=repo,
67
- repo_type="dataset", commit_message=f"stage {key}: {len(files)} shard(s)",
68
- allow_patterns=["*.bin", "*.json"])
69
- return files
70
-
71
-
72
- def download_missing(root, keys_wanted, repo, remote=None):
73
- """Fetch stage/<key>/*.bin for every wanted key not already on disk. Returns {key: [filenames]}."""
74
- from huggingface_hub import hf_hub_download
75
-
76
- remote = remote if remote is not None else remote_stage_files(repo)
77
- got = {}
78
- for key in keys_wanted:
79
- want = sorted(p for p in remote if p.startswith(f"stage/{key}/") and p.endswith(".bin"))
80
- if not want:
81
- got[key] = []
82
- continue
83
- local_dir = os.path.join(root, "stage", key)
84
- os.makedirs(local_dir, exist_ok=True)
85
- have = {f"stage/{key}/{f}" for f in os.listdir(local_dir)}
86
- for rp in want:
87
- if rp in have:
88
- continue
89
- hf_hub_download(repo_id=repo, repo_type="dataset", filename=rp,
90
- local_dir=root, force_download=False)
91
- got[key] = sorted(os.listdir(local_dir))
92
- return got
93
-
94
-
95
- def status(repo):
96
- import urllib.request
97
-
98
- url = f"https://huggingface.co/api/datasets/{repo}"
99
- try:
100
- with urllib.request.urlopen(url, timeout=60) as r:
101
- d = json.loads(r.read().decode())
102
- return {"exists": True, "gated": d.get("gated"), "private": d.get("private"),
103
- "lastModified": d.get("lastModified"), "id": d.get("id")}
104
- except Exception as e:
105
- return {"exists": False, "error": f"{type(e).__name__}: {str(e)[:120]}"}
 
 
 
 
 
 
 
 
 
1
+ # Hub sync for the mix build: the instance is scratch, so nothing that took an hour to produce may live
2
+ # only there (§3.13 "never let the instance be the only copy", applied to a CPU job rather than a
3
+ # checkpoint).
4
+ #
5
+ # Granularity is one *source*: staging a source takes 5-40 minutes, so publishing its shards the moment
6
+ # it finishes bounds the loss from any interruption to the one source in flight, and a resume can skip
7
+ # every source already on the Hub without re-reading it.
8
+ #
9
+ # The stage repo is private-in-practice but public by project rule; it holds no credentials and no
10
+ # benchmark material -- only token ids from the approved mix.
11
+
12
+ import json
13
+ import os
14
+
15
+
16
+ def repo_id_from_env(default=None):
17
+ return os.environ.get("OUNCE100M_STAGE_REPO") or default
18
+
19
+
20
+ def api():
21
+ import os
22
+
23
+ import ounce100m_credentials
24
+
25
+ ounce100m_credentials.install()
26
+ from huggingface_hub import HfApi
27
+
28
+ return HfApi(token=os.environ["HF_TOKEN"])
29
+
30
+
31
+ def ensure_repo(repo):
32
+ # Must install() first: the module-level create_repo() resolves its token from the environment, and
33
+ # this is the FIRST hub call a build makes -- before any api() has run. Calling it unauthenticated
34
+ # is what produced the misleading 401 in the cold-resume rehearsal (E-017), and the diagnosis in
35
+ # these notes was wrong about the cause until p2-token-scope proved dataset writes are permitted.
36
+ import os
37
+
38
+ api()
39
+ from huggingface_hub import create_repo
40
+
41
+ # hub 1.11 (the image's version) takes the owner inside repo_id and has no `namespace` kwarg (E-013)
42
+ create_repo(repo_id=repo, repo_type="dataset", private=False, exist_ok=True,
43
+ token=os.environ["HF_TOKEN"])
44
+ return repo
45
+
46
+
47
+ def remote_stage_files(repo):
48
+ """Set of relative paths under stage/ that already exist on the Hub."""
49
+ import urllib.request
50
+
51
+ url = f"https://huggingface.co/api/datasets/{repo}/tree/main/stage?recursive=true"
52
+ try:
53
+ with urllib.request.urlopen(url, timeout=90) as r:
54
+ entries = json.loads(r.read().decode())
55
+ except Exception:
56
+ return set()
57
+ out = set()
58
+ for e in entries:
59
+ if e.get("type") != "file":
60
+ continue
61
+ out.add(e["path"])
62
+ # a directory in the tree listing means we must recurse; recursive=true should cover it, but
63
+ # if the server ignored the flag we get dirs and no files, which the caller detects as empty
64
+ return out
65
+
66
+
67
+ def upload_source(root, key, repo):
68
+ """Publish stage/<key>/* in one commit. Returns the file names pushed."""
69
+ a = api()
70
+ local = os.path.join(root, "stage", key)
71
+ files = sorted(f for f in os.listdir(local) if f.endswith((".bin", ".json")))
72
+ if not files:
73
+ return []
74
+ a.upload_folder(folder_path=local, path_in_repo=f"stage/{key}", repo_id=repo,
75
+ repo_type="dataset", commit_message=f"stage {key}: {len(files)} shard(s)",
76
+ allow_patterns=["*.bin", "*.json"])
77
+ return files
78
+
79
+
80
+ def download_missing(root, keys_wanted, repo, remote=None):
81
+ """Fetch stage/<key>/*.bin for every wanted key not already on disk. Returns {key: [filenames]}."""
82
+ from huggingface_hub import hf_hub_download
83
+
84
+ remote = remote if remote is not None else remote_stage_files(repo)
85
+ got = {}
86
+ for key in keys_wanted:
87
+ want = sorted(p for p in remote if p.startswith(f"stage/{key}/") and p.endswith(".bin"))
88
+ if not want:
89
+ got[key] = []
90
+ continue
91
+ local_dir = os.path.join(root, "stage", key)
92
+ os.makedirs(local_dir, exist_ok=True)
93
+ have = {f"stage/{key}/{f}" for f in os.listdir(local_dir)}
94
+ for rp in want:
95
+ if rp in have:
96
+ continue
97
+ hf_hub_download(repo_id=repo, repo_type="dataset", filename=rp,
98
+ local_dir=root, force_download=False)
99
+ got[key] = sorted(os.listdir(local_dir))
100
+ return got
101
+
102
+
103
+ def status(repo):
104
+ import urllib.request
105
+
106
+ url = f"https://huggingface.co/api/datasets/{repo}"
107
+ try:
108
+ with urllib.request.urlopen(url, timeout=60) as r:
109
+ d = json.loads(r.read().decode())
110
+ return {"exists": True, "gated": d.get("gated"), "private": d.get("private"),
111
+ "lastModified": d.get("lastModified"), "id": d.get("id")}
112
+ except Exception as e:
113
+ return {"exists": False, "error": f"{type(e).__name__}: {str(e)[:120]}"}