Skip to content

Commit 9d854f6

Browse files
committed
feat(data): add base16k repeat sampler
1 parent ac404f8 commit 9d854f6

2 files changed

Lines changed: 501 additions & 0 deletions

File tree

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
#!/usr/bin/env python3
2+
"""Export carefully repeated base-library functions into a 16k-only stream.
3+
4+
The normal code/commit streams use ``semantic_chunk:v1`` claims with
5+
``max_count=1`` so exact function/class/type forms do not overlap across
6+
1024/2048/4096/8192 buckets. This script is the explicit exception lane: it
7+
samples foundational base-library function bodies from the global cross-repo
8+
symbol index and emits them ONLY into a 16384-token output stream, while using
9+
the SAME claim namespace with ``max_count=3``.
10+
11+
That means the global invariant is:
12+
13+
* normal streams: a semantic chunk form can appear once;
14+
* base16k stream: selected base functions may top up the same form to at most
15+
three total appearances across the whole training period.
16+
17+
No fallback path: the symbol DB, dedup DB, tokenizer, materializer and packer
18+
must all work or the script raises.
19+
"""
20+
from __future__ import annotations
21+
22+
import argparse
23+
import json
24+
import sqlite3
25+
import sys
26+
import tempfile
27+
from dataclasses import dataclass
28+
from pathlib import Path
29+
30+
31+
REPO_ROOT = Path(__file__).resolve().parents[2]
32+
if str(REPO_ROOT) not in sys.path:
33+
sys.path.insert(0, str(REPO_ROOT))
34+
if str(REPO_ROOT / "scripts") not in sys.path:
35+
sys.path.insert(0, str(REPO_ROOT / "scripts"))
36+
if str(REPO_ROOT / "tools" / "clang_indexer") not in sys.path:
37+
sys.path.insert(0, str(REPO_ROOT / "tools" / "clang_indexer"))
38+
39+
import streaming_reindex as sr # noqa: E402
40+
from streaming_reindex_commits import recompress_zstd_max # noqa: E402
41+
from cppmega_mlx.tokenizer.cpp_tokenizer import load_cppmega_tokenizer # noqa: E402
42+
from dedup_store import DedupStore # noqa: E402
43+
44+
45+
DEFAULT_SYMBOL_INDEX = REPO_ROOT / "outputs" / "crossrepo" / "global_symbols.sqlite"
46+
DEFAULT_DEDUP_DB = REPO_ROOT / "outputs" / "dedup_seen.sqlite"
47+
DEFAULT_OUTPUT_ROOT = REPO_ROOT / "outputs" / "reindexed_base16k"
48+
SEMANTIC_CHUNK_NAMESPACE = "semantic_chunk:v1"
49+
TARGET_LENGTH = 16384
50+
BASE_LIB_ORDER = {
51+
"boost": 0,
52+
"abseil": 1,
53+
"folly": 2,
54+
"openssl": 3,
55+
"boringssl": 4,
56+
"protobuf": 5,
57+
"eigen": 6,
58+
"fmt": 7,
59+
"glib": 8,
60+
"std": 9,
61+
"libc": 10,
62+
}
63+
64+
65+
@dataclass(frozen=True)
66+
class BaseSymbol:
67+
qname: str
68+
base_lib: str
69+
base_repo: str
70+
kind: int
71+
sym_type: str
72+
file: str
73+
line: int
74+
token_est: int
75+
body_len: int
76+
text: str
77+
78+
79+
def _connect_symbol_index(path: Path) -> sqlite3.Connection:
80+
if not path.exists():
81+
raise FileNotFoundError(f"--symbol-index does not exist: {path}")
82+
conn = sqlite3.connect(f"file:{path}?mode=ro", uri=True)
83+
conn.row_factory = sqlite3.Row
84+
return conn
85+
86+
87+
def load_candidate_symbols(
88+
symbol_index: Path,
89+
*,
90+
libs: tuple[str, ...],
91+
limit: int,
92+
offset: int,
93+
min_body_len: int,
94+
max_token_est: int,
95+
) -> list[BaseSymbol]:
96+
conn = _connect_symbol_index(symbol_index)
97+
try:
98+
params: list[object] = [int(min_body_len), int(max_token_est)]
99+
where = [
100+
"sym_type='func'",
101+
"body_len >= ?",
102+
"token_est <= ?",
103+
"text IS NOT NULL",
104+
"length(text) > 0",
105+
]
106+
if libs:
107+
where.append("base_lib IN (%s)" % ",".join("?" for _ in libs))
108+
params.extend(libs)
109+
sql = (
110+
"SELECT qname, base_lib, base_repo, kind, sym_type, file, line, "
111+
"token_est, body_len, text "
112+
"FROM symbols WHERE "
113+
+ " AND ".join(where)
114+
)
115+
rows = list(conn.execute(sql, params))
116+
finally:
117+
conn.close()
118+
119+
symbols = [
120+
BaseSymbol(
121+
qname=str(r["qname"]),
122+
base_lib=str(r["base_lib"]),
123+
base_repo=str(r["base_repo"]),
124+
kind=int(r["kind"]),
125+
sym_type=str(r["sym_type"]),
126+
file=str(r["file"]),
127+
line=int(r["line"]),
128+
token_est=int(r["token_est"]),
129+
body_len=int(r["body_len"]),
130+
text=str(r["text"]),
131+
)
132+
for r in rows
133+
]
134+
symbols.sort(
135+
key=lambda s: (
136+
BASE_LIB_ORDER.get(s.base_lib, 100),
137+
-s.body_len,
138+
-s.token_est,
139+
s.qname,
140+
s.file,
141+
s.line,
142+
)
143+
)
144+
end = None if limit < 0 else offset + limit
145+
return symbols[offset:end]
146+
147+
148+
def _base_doc(symbol: BaseSymbol, *, repeat_index: int, token_count: int) -> dict:
149+
text = symbol.text
150+
provenance = [{
151+
"kind": "base16k_repeat",
152+
"qname": symbol.qname,
153+
"base_lib": symbol.base_lib,
154+
"base_repo": symbol.base_repo,
155+
"file": symbol.file,
156+
"line": symbol.line,
157+
"repeat_index": repeat_index,
158+
}]
159+
return {
160+
"text": text,
161+
"source_text": text,
162+
"source_doc_id": (
163+
f"base16k:{symbol.base_lib}:{symbol.qname}:r{repeat_index}"
164+
),
165+
"actual_token_count": token_count,
166+
"structure_ids": [int(symbol.kind)] * len(text),
167+
"chunk_boundaries": [{
168+
"start": 0,
169+
"end": len(text),
170+
"kind": int(symbol.kind),
171+
"dep_level": 0,
172+
"name": symbol.qname.split("::")[-1],
173+
}],
174+
"call_edges": [],
175+
"type_edges": [],
176+
"ast_depth": [],
177+
"sibling_index": [],
178+
"ast_node_type": [],
179+
"symbol_ids": [],
180+
"call_targets": [],
181+
"type_refs": [],
182+
"def_use": [],
183+
"repo": symbol.base_repo,
184+
"filepath": symbol.file,
185+
"doc_type": "base16k_repeat",
186+
"language": "cpp",
187+
"constituent_provenance_json": json.dumps(
188+
provenance,
189+
separators=(",", ":"),
190+
),
191+
}
192+
193+
194+
def build_repeat_docs(
195+
symbols: list[BaseSymbol],
196+
*,
197+
tokenizer_path: Path,
198+
dedup_db: Path,
199+
repeats: int,
200+
max_count: int,
201+
target_length: int = TARGET_LENGTH,
202+
) -> tuple[list[dict], dict[str, int]]:
203+
if repeats < 1:
204+
raise ValueError("--repeats must be >= 1")
205+
if max_count < 1:
206+
raise ValueError("--max-count must be >= 1")
207+
tok = load_cppmega_tokenizer(str(tokenizer_path))
208+
store = DedupStore(str(dedup_db), near=False, commit_every=1)
209+
docs: list[dict] = []
210+
stats = {
211+
"candidate_symbols": len(symbols),
212+
"symbols_emitted": 0,
213+
"symbols_saturated": 0,
214+
"docs_emitted": 0,
215+
"claims_rejected": 0,
216+
"too_long": 0,
217+
"empty": 0,
218+
}
219+
try:
220+
for symbol in symbols:
221+
token_ids = tok.encode(symbol.text)
222+
if not token_ids:
223+
stats["empty"] += 1
224+
continue
225+
if len(token_ids) > target_length:
226+
stats["too_long"] += 1
227+
continue
228+
emitted_for_symbol = 0
229+
for repeat_index in range(int(repeats)):
230+
ok = store.claim_chunk_tokens(
231+
token_ids,
232+
namespace=SEMANTIC_CHUNK_NAMESPACE,
233+
max_count=int(max_count),
234+
)
235+
if not ok:
236+
stats["claims_rejected"] += 1
237+
break
238+
docs.append(
239+
_base_doc(
240+
symbol,
241+
repeat_index=repeat_index,
242+
token_count=len(token_ids),
243+
)
244+
)
245+
emitted_for_symbol += 1
246+
if emitted_for_symbol:
247+
stats["symbols_emitted"] += 1
248+
stats["docs_emitted"] += emitted_for_symbol
249+
else:
250+
stats["symbols_saturated"] += 1
251+
finally:
252+
store.close()
253+
return docs, stats
254+
255+
256+
def _write_jsonl(docs: list[dict], path: Path) -> None:
257+
if not docs:
258+
raise RuntimeError("base16k sampler emitted zero docs")
259+
with path.open("w", encoding="utf-8") as fh:
260+
for doc in docs:
261+
fh.write(json.dumps(doc, ensure_ascii=False, separators=(",", ":")))
262+
fh.write("\n")
263+
264+
265+
def _append_output(shard_name: str, packed: Path, output_root: Path) -> dict:
266+
out_dir = output_root / str(TARGET_LENGTH)
267+
out_dir.mkdir(parents=True, exist_ok=True)
268+
dest = out_dir / f"{shard_name}.parquet"
269+
dest.write_bytes(packed.read_bytes())
270+
recompress_zstd_max(dest)
271+
return sr._parquet_stats(dest, TARGET_LENGTH)
272+
273+
274+
def export_base16k(args: argparse.Namespace) -> dict:
275+
symbol_index = Path(args.symbol_index)
276+
dedup_db = Path(args.dedup_db)
277+
tokenizer_path = Path(args.tokenizer_path)
278+
output_root = Path(args.output_root)
279+
libs = tuple(x.strip() for x in args.libs.split(",") if x.strip())
280+
shard_name = args.shard_name or f"base16k_{int(args.offset):08d}"
281+
282+
symbols = load_candidate_symbols(
283+
symbol_index,
284+
libs=libs,
285+
limit=int(args.limit),
286+
offset=int(args.offset),
287+
min_body_len=int(args.min_body_len),
288+
max_token_est=int(args.max_token_est),
289+
)
290+
docs, stats = build_repeat_docs(
291+
symbols,
292+
tokenizer_path=tokenizer_path,
293+
dedup_db=dedup_db,
294+
repeats=int(args.repeats),
295+
max_count=int(args.max_count),
296+
target_length=TARGET_LENGTH,
297+
)
298+
299+
with tempfile.TemporaryDirectory(prefix="base16k_export_") as tmp:
300+
work = Path(tmp)
301+
jsonl = work / f"{shard_name}.jsonl"
302+
_write_jsonl(docs, jsonl)
303+
tok = sr.stage_materialize(
304+
shard_name,
305+
jsonl,
306+
work,
307+
memory_limit_gb=float(args.memory_limit_gb),
308+
)
309+
packed = sr.stage_pack(shard_name, tok, TARGET_LENGTH, work)
310+
parquet_stats = _append_output(shard_name, packed, output_root)
311+
312+
return {
313+
"source": "base16k",
314+
"symbol_index": str(symbol_index),
315+
"dedup_db": str(dedup_db),
316+
"output_root": str(output_root),
317+
"shard": shard_name,
318+
"target_length": TARGET_LENGTH,
319+
"libs": list(libs),
320+
"stats": stats,
321+
"lengths": {str(TARGET_LENGTH): parquet_stats},
322+
}
323+
324+
325+
def parse_args(argv: list[str]) -> argparse.Namespace:
326+
p = argparse.ArgumentParser(description=__doc__)
327+
p.add_argument("--symbol-index", default=str(DEFAULT_SYMBOL_INDEX))
328+
p.add_argument("--dedup-db", default=str(DEFAULT_DEDUP_DB))
329+
p.add_argument(
330+
"--tokenizer-path",
331+
default=str(REPO_ROOT / "cppmega_mlx" / "tokenizer" / "tokenizer.json"),
332+
)
333+
p.add_argument("--output-root", default=str(DEFAULT_OUTPUT_ROOT))
334+
p.add_argument(
335+
"--libs",
336+
default="boost,abseil,folly,openssl,boringssl,protobuf,eigen,fmt,glib,std,libc",
337+
help="Comma-separated base_lib keys to sample from.",
338+
)
339+
p.add_argument("--offset", type=int, default=0)
340+
p.add_argument("--limit", type=int, default=1000)
341+
p.add_argument("--repeats", type=int, default=3)
342+
p.add_argument("--max-count", type=int, default=3)
343+
p.add_argument("--min-body-len", type=int, default=200)
344+
p.add_argument("--max-token-est", type=int, default=TARGET_LENGTH)
345+
p.add_argument("--memory-limit-gb", type=float, default=10.0)
346+
p.add_argument("--shard-name", default=None)
347+
return p.parse_args(argv)
348+
349+
350+
def main(argv: list[str]) -> int:
351+
args = parse_args(argv)
352+
print(json.dumps(export_base16k(args), indent=2, sort_keys=True))
353+
return 0
354+
355+
356+
if __name__ == "__main__":
357+
raise SystemExit(main(sys.argv[1:]))

0 commit comments

Comments
 (0)