From 2416bc90794f946b79332db3d963cb7f4bd474e8 Mon Sep 17 00:00:00 2001 From: Sidd <86228410+beiyonder@users.noreply.github.com> Date: Sun, 12 Jul 2026 17:08:40 +0530 Subject: [PATCH] Translate user-facing runtime output to English, machine prefixes preserved, + CLI output test (#67, #85) * feat: standardize runtime output in English * test: cover English CLI output --- README.md | 8 +- assets/colibri.svg | 4 +- c/Makefile | 2 +- c/coli | 118 +++++++++---------- c/glm.c | 152 ++++++++++++------------- c/grammar.h | 50 ++++---- c/iobench.c | 10 +- c/olmoe.c | 20 ++-- c/scripts/run.sh | 14 +-- c/scripts/supervisor.sh | 12 +- c/setup.sh | 34 +++--- c/st.h | 10 +- c/tests/test_benchmark_cuda_fixture.py | 2 +- c/tests/test_cli_output.py | 49 ++++++++ c/tests/test_tok.c | 4 +- c/tok.h | 2 +- c/tok_unicode.h | 2 +- c/tools/benchmark_cuda_fixture.py | 4 +- c/tools/convert_fp8_to_int4.py | 63 +++++----- c/tools/download_glm52.py | 12 +- c/tools/eval_glm.py | 30 ++--- c/tools/fetch_benchmarks.py | 2 +- c/tools/gen_unicode.py | 2 +- c/tools/make_glm_oracle.py | 4 +- 24 files changed, 330 insertions(+), 280 deletions(-) create mode 100644 c/tests/test_cli_output.py diff --git a/README.md b/README.md index f4a9ace..3d2328e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- colibrì — piccolo motore, modello immenso + colibrì — tiny engine, immense model

**Tiny engine, immense model.** Run **GLM-5.2 (744B-parameter MoE)** on a consumer machine with ~25 GB of RAM — in pure C, with zero dependencies, by streaming experts from disk. @@ -7,7 +7,7 @@ ``` $ ./coli chat 🐦 colibrì v1.0 — GLM-5.2 · 744B MoE · int4 · streaming CPU - ✓ pronto in 32s · residente 9.9 GB + ✓ ready in 32s · resident 9.9 GB › ciao! ◆ Ciao! 😊 Come posso aiutarti oggi? ``` @@ -154,7 +154,7 @@ make test-python # run Python tests (requires python) # Verify (tiny model, 2.4 MB): pip install torch transformers safetensors huggingface_hub python tools/make_glm_oracle.py # generate tiny oracle -SNAP=./glm_tiny TF=1 ./glm.exe 64 16 16 # expect "32/32 posizioni" +SNAP=./glm_tiny TF=1 ./glm.exe 64 16 16 # expect "32/32 positions" # Run with real model: SNAP=D:\glm52_i4 ./glm.exe 64 4 16 # batch inference @@ -313,7 +313,7 @@ works against the colibrì OpenAI-compatible server (in review, #21) or any othe compatible endpoint. Nothing leaves the endpoint you configure. The terminal `coli chat` remains the first-class interface. -Useful knobs (env or flags): `--temp T` token sampling temperature (default 0.7 + nucleus 0.90 — tuned for int4; 0 = greedy), `--topp 0.7` adaptive expert top-p (30–40% less disk), `--ngen N` max tokens per answer (`:piu` in chat continues a truncated one), `--repin N` adapt RAM/VRAM hot experts every N emitted tokens, `AUTOPIN=0` disable the learning cache's auto-pin, `THINK=1` enable GLM-5.2's reasoning block, `DRAFT=n` MTP draft depth, `GRAMMAR=g.gbnf` grammar-forced drafts for constrained JSON/NDJSON output (`GRAMMAR_DRAFT=n` caps the forced span), `TF=1` teacher-forcing validation, `PILOT=1` router-lookahead disk prefetch (experimental — see below), `CAP_RAISE=0` don't auto-grow the expert cache. +Useful knobs (env or flags): `--temp T` token sampling temperature (default 0.7 + nucleus 0.90 — tuned for int4; 0 = greedy), `--topp 0.7` adaptive expert top-p (30–40% less disk), `--ngen N` max tokens per answer (`:more` in chat continues a truncated one), `--repin N` adapt RAM/VRAM hot experts every N emitted tokens, `AUTOPIN=0` disable the learning cache's auto-pin, `THINK=1` enable GLM-5.2's reasoning block, `DRAFT=n` MTP draft depth, `GRAMMAR=g.gbnf` grammar-forced drafts for constrained JSON/NDJSON output (`GRAMMAR_DRAFT=n` caps the forced span), `TF=1` teacher-forcing validation, `PILOT=1` router-lookahead disk prefetch (experimental — see below), `CAP_RAISE=0` don't auto-grow the expert cache. **The expert cache auto-sizes to your RAM** (since 2026-07-10): the engine now *raises* the LRU cap to fill your `--ram` budget instead of only lowering it. Before this fix a 128 GB machine ran with the same 8-experts/layer cache as a 16 GB one (issue #12) — **if you benchmarked colibrì before this date, rerun: your numbers were capped.** diff --git a/assets/colibri.svg b/assets/colibri.svg index d4ddeb6..4aed0bb 100644 --- a/assets/colibri.svg +++ b/assets/colibri.svg @@ -49,7 +49,7 @@ colibrì piccolo motore, modello immenso + font-size="19" fill="#808080" font-style="italic">tiny engine, immense model GLM-5.2 · 744B MoE · int4 · streaming CPU - \ No newline at end of file + diff --git a/c/Makefile b/c/Makefile index 77bdf63..c23a8db 100644 --- a/c/Makefile +++ b/c/Makefile @@ -14,7 +14,7 @@ ifneq ($(OMPDIR),) OMPC = -Xclang -fopenmp -I$(OMPDIR)/include OMPL = -L$(OMPDIR)/lib -lomp else -$(warning libomp non trovato: build SINGLE-THREAD. Per il multithread: brew install libomp) +$(warning libomp not found: building single-threaded. For multithreading: brew install libomp) OMPC = OMPL = endif diff --git a/c/coli b/c/coli index f9e601e..e4e89ea 100755 --- a/c/coli +++ b/c/coli @@ -1,24 +1,24 @@ #!/usr/bin/env python3 """ -colibrì — piccolo motore, modello immenso. -CLI per far girare GLM-5.2 (744B) in locale, su CPU, in ~15-26 GB di RAM. +colibrì — tiny engine, immense model. +Run GLM-5.2 (744B) locally on CPU with roughly 15-26 GB of RAM. - coli chat chat interattiva (carica il modello UNA volta) - coli serve API HTTP compatibile OpenAI (motore persistente) - coli run "prompt" generazione singola - coli info stato: modello, RAM, disco, config - coli plan piano risorse Disk / RAM / VRAM - coli doctor diagnosi installazione e piano di esecuzione - coli bench [task...] benchmark di qualità (MMLU/HellaSwag/...) - coli convert converte GLM-5.2-FP8 -> int4 (streaming) - coli build compila il motore + coli chat interactive chat (loads the model once) + coli serve OpenAI-compatible HTTP API (persistent engine) + coli run "prompt" one-shot generation + coli info model, RAM, disk, and configuration status + coli plan Disk / RAM / VRAM resource plan + coli doctor installation and execution-plan diagnostics + coli bench [task...] quality benchmarks (MMLU/HellaSwag/...) + coli convert convert GLM-5.2-FP8 to int4, one shard at a time + coli build build the engine -Config via env o flag (validi anche dopo il sottocomando): - COLI_MODEL= modello (default /home/vincenzo/glm52_i4) - --ram N budget RAM in GB (auto-cap cache expert) - --repin N adatta gli expert RAM/VRAM ogni N token - --topp P top-p adattivo sugli expert --topk N top-k fisso - --ngen N token massimi per risposta --cap N slot cache/layer +Configuration through environment variables or flags (also valid after the subcommand): + COLI_MODEL= model directory (default /home/vincenzo/glm52_i4) + --ram N RAM budget in GB (automatically sizes the expert cache) + --repin N adapt RAM/VRAM experts every N tokens + --topp P adaptive expert top-p --topk N fixed top-k + --ngen N maximum response tokens --cap N cache slots/layer """ import os, sys, subprocess, argparse, json, time, signal, shutil, threading, re, codecs, tempfile, textwrap @@ -82,7 +82,7 @@ def banner(sub=""): sp=sprite_lines() txt=[ f"{C.teal}{C.b}colibrì{C.r} {C.dim}v1.0{C.r}", - f"{C.dim}piccolo motore, modello immenso{C.r}", + f"{C.dim}tiny engine, immense model{C.r}", f"{C.gray}GLM-5.2 · 744B MoE · int4 · streaming CPU{C.r}", f"{C.dgray}{sub}{C.r}" if sub else "", "", @@ -100,11 +100,11 @@ def term_w(): return min(shutil.get_terminal_size((80,20)).columns, 100) def need_model(model): if not os.path.isdir(model): - sys.exit(f"{C.yel}modello non trovato:{C.r} {model}\n imposta COLI_MODEL o usa --model") + sys.exit(f"{C.yel}model not found:{C.r} {model}\n set COLI_MODEL or use --model") if not os.path.exists(os.path.join(model,"tokenizer.json")): - sys.exit(f"{C.yel}manca tokenizer.json in {model}{C.r}") + sys.exit(f"{C.yel}tokenizer.json is missing from {model}{C.r}") if not os.path.exists(GLM): - sys.exit(f"{C.yel}motore non compilato.{C.r} Esegui: coli build") + sys.exit(f"{C.yel}engine is not built.{C.r} Run: coli build") def cuda_binary(): if not os.path.exists(GLM) or sys.platform != "linux": return False @@ -149,7 +149,7 @@ def env_for(a): ram,ctx,devices,vram=resource_request(a,e) plan=build_plan(a.model,ram,ctx,devices,vram) except (OSError,ValueError,json.JSONDecodeError) as error: - sys.exit(f"{C.yel}piano risorse non valido:{C.r} {error}") + sys.exit(f"{C.yel}invalid resource plan:{C.r} {error}") has_cuda=cuda_binary() e=environment_for_plan(plan,e,has_cuda) rt=plan["tiers"]["ram"]; vt=plan["tiers"]["vram"] @@ -292,26 +292,26 @@ def cmd_info(a): def row(k,v): print(f" {C.gray}{k:<10}{C.r} {v}") if os.path.exists(cfgp): c=json.load(open(cfgp)) - row("modello", a.model) + row("model", a.model) row("arch", f"hidden {c.get('hidden_size')} · {c.get('num_hidden_layers')} layer · " f"{c.get('n_routed_experts')} expert/layer · top-{c.get('num_experts_per_tok')}") sts=[x for x in os.listdir(a.model) if x.endswith('.safetensors')] sz=sum(os.path.getsize(os.path.join(a.model,x)) for x in sts) - row("shard", f"{len(sts)} file · {sz/1e9:.0f} GB su disco") + row("shards", f"{len(sts)} files · {sz/1e9:.0f} GB on disk") else: - print(f" {C.yel}config.json non presente (conversione incompleta?){C.r}") + print(f" {C.yel}config.json is missing (incomplete conversion?){C.r}") try: mi=open('/proc/meminfo').read() tot=int(re.search(r'MemTotal:\s+(\d+)',mi).group(1))/1e6 av=int(re.search(r'MemAvailable:\s+(\d+)',mi).group(1))/1e6 - row("RAM", f"{tot:.0f} GB totali · {av:.1f} GB disponibili") + row("RAM", f"{tot:.0f} GB total · {av:.1f} GB available") except Exception: pass try: fs = shutil.disk_usage(a.model if os.path.isdir(a.model) else HERE) - row("disco", f"{fs.free/1e9:.0f} GB liberi") + row("disk", f"{fs.free/1e9:.0f} GB free") except OSError: - row("disco", "? GB (non disponibile)") - row("motore", "pronto ✓" if os.path.exists(GLM) else "da compilare (coli build)") + row("disk", "? GB (unavailable)") + row("engine", "ready ✓" if os.path.exists(GLM) else "not built (coli build)") knobs=[] if a.ram: knobs.append(f"ram {a.ram}GB") if a.topp: knobs.append(f"topp {a.topp}") @@ -323,11 +323,11 @@ def cmd_plan(a): from resource_plan import build_plan, format_plan try: ram,ctx,devices,vram=resource_request(a,os.environ) - if ctx<1: raise ValueError("--ctx deve essere positivo") - if a.vram<0: raise ValueError("--vram non puo essere negativo") + if ctx<1: raise ValueError("--ctx must be positive") + if a.vram<0: raise ValueError("--vram cannot be negative") plan=build_plan(a.model,ram,ctx,devices,vram) except (OSError, ValueError, json.JSONDecodeError) as error: - sys.exit(f"{C.yel}impossibile creare il piano:{C.r} {error}") + sys.exit(f"{C.yel}cannot create resource plan:{C.r} {error}") if a.json: print(json.dumps(plan,indent=2)) return @@ -339,9 +339,9 @@ def cmd_doctor(a): from doctor import exit_code, format_doctor, run_doctor try: ram,ctx,devices,vram=resource_request(a,os.environ) - if ctx<1: raise ValueError("--ctx deve essere positivo") - if ram<0: raise ValueError("--ram non puo essere negativo") - if vram<0: raise ValueError("--vram non puo essere negativo") + if ctx<1: raise ValueError("--ctx must be positive") + if ram<0: raise ValueError("--ram cannot be negative") + if vram<0: raise ValueError("--vram cannot be negative") except ValueError as error: report={"schema_version":1,"status":"error","model":os.path.abspath(a.model), "checks":[{"id":"config.arguments","status":"fail","summary":str(error)}], @@ -354,7 +354,7 @@ def cmd_doctor(a): def cmd_run(a): need_model(a.model) - prompt=" ".join(a.prompt) if a.prompt else sys.exit('uso: coli run "il tuo prompt"') + prompt=" ".join(a.prompt) if a.prompt else sys.exit('usage: coli run "your prompt"') banner("run") # template ufficiale GLM-5.2: niente \n dopo i ruoli; = risposta diretta (nothink) e=env_for(a); e["PROMPT"]=f"[gMASK]<|user|>{prompt}<|assistant|>" @@ -367,24 +367,24 @@ def cmd_chat(a): e=env_for(a); e["SERVE"]="1" p=subprocess.Popen([GLM,str(a.cap)], env=e, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=errlog, bufsize=0) - sp=Spinner("sveglio il gigante (744B)…"); sp.start() + sp=Spinner("waking the giant (744B)…"); sp.start() st=stream_turn(p, READY, lambda b: None) sp.stop() if st is None: - errlog.seek(0); print(errlog.read()[-1500:]); sys.exit("il motore è uscito durante il load") + errlog.seek(0); print(errlog.read()[-1500:]); sys.exit("the engine exited while loading") errlog.flush() try: elog=open(errlog.name).read() - mload=re.search(r"caricato in ([0-9.]+)s \| densa residente: ([0-9.]+) MB", elog) - if mload: print(f" {C.grn}✓{C.r} pronto in {mload.group(1)}s {C.dim}· residente {float(mload.group(2))/1000:.1f} GB · RSS {st.get('rss','?')} GB{C.r}") + mload=re.search(r"loaded in ([0-9.]+)s \| resident dense: ([0-9.]+) MB", elog) + if mload: print(f" {C.grn}✓{C.r} ready in {mload.group(1)}s {C.dim}· resident {float(mload.group(2))/1000:.1f} GB · RSS {st.get('rss','?')} GB{C.r}") for l in elog.splitlines(): # una riga di stato per riga, senza path if l.startswith(("[RAM_GB","[PIN]","[MTP]","[USAGE]","[DSA]","[KV]")): l=re.sub(r" ?\(?/[^ )]+\)?","",l.strip()) # via i percorsi lunghi - l=re.sub(r" da$| in$","",l) + l=re.sub(r" from$","",l) for chunk in textwrap.wrap(l, term_w()-4) or [l]: print(f" {C.dgray}{chunk}{C.r}") except Exception: pass - print(f" {C.dim}scrivi e premi invio · :piu continua risposta · :reset memoria · :q esci{C.r}\n") + print(f" {C.dim}type and press Enter · :more continues · :reset clears memory · :q exits{C.r}\n") w=term_w()-4 def user_box(msg): """ri-disegna il messaggio dentro una box che si ADATTA su piu' righe: @@ -415,7 +415,7 @@ def cmd_chat(a): if msg==":reset": p.stdin.write(b"\x02RESET\n"); p.stdin.flush() stream_turn(p, END, lambda b: None) - print(f" {C.dim}✦ memoria azzerata{C.r}\n"); continue + print(f" {C.dim}✦ memory cleared{C.r}\n"); continue if msg in (":piu",":più",":more",":continua"): p.stdin.write(b"\x02MORE\n"); p.stdin.flush() else: @@ -430,7 +430,7 @@ def cmd_chat(a): pl=[l for l in tail.splitlines() if l.startswith("[prefill]")] return pl[-1].replace("[prefill] ","prefill ") if pl else "" except Exception: return "" - sp2=Spinner("pensa…", tick=prefill_tick); sp2.start() + sp2=Spinner("thinking…", tick=prefill_tick); sp2.start() md=MDStream(" ") # markdown -> terminale, in streaming raw=os.environ.get("COLI_RAW")=="1" def echo(bs, _dec=dec, _st=state): @@ -445,23 +445,23 @@ def cmd_chat(a): st=stream_turn(p, END, echo) if not raw: md.close() sp2.stop() - if st is None: print(f"\n {C.yel}[motore terminato]{C.r}"); break + if st is None: print(f"\n {C.yel}[engine terminated]{C.r}"); break el=time.time()-t0 if st.get("tok"): print(f"\r {C.dgray}└─ {st['tok']} tok · {st['tps']:.2f} tok/s · hit {st['hit']:.0f}% · RSS {st['rss']:.1f} GB · {el:.0f}s{C.r}") if st["tok"]>=a.ngen: - print(f" {C.yel}…troncato al limite --ngen ({a.ngen}): scrivi :piu per far continuare la risposta{C.r}") + print(f" {C.yel}…stopped at --ngen ({a.ngen}); type :more to continue the response{C.r}") print() else: print() except KeyboardInterrupt: - print(f"\n {C.dim}interrotto{C.r}") + print(f"\n {C.dim}interrupted{C.r}") finally: try: p.stdin.close(); p.terminate() except Exception: pass try: os.unlink(errlog.name) except Exception: pass - print(f" {C.teal}ciao{C.r} {C.dim}— il colibrì torna al nido{C.r} 🐦\n") + print(f" {C.teal}goodbye{C.r} {C.dim}— the hummingbird returns to its nest{C.r} 🐦\n") def cmd_serve(a): need_model(a.model) @@ -480,14 +480,14 @@ def cmd_bench(a): # dataset mancanti -> li scarica una volta (fetch_benchmarks.py li mette in --data come JSONL) missing=[t for t in tasks.split(",") if not os.path.exists(os.path.join(a.data,f"{t}.jsonl"))] if missing: - print(f" {C.dim}scarico i dataset mancanti: {', '.join(missing)}{C.r}") + print(f" {C.dim}downloading missing datasets: {', '.join(missing)}{C.r}") subprocess.call([py, os.path.join(TOOLS,"fetch_benchmarks.py"), "--out", a.data, "--tasks", ",".join(missing), "--limit", str(max(a.limit,200))]) cmd=[py, os.path.join(TOOLS,"eval_glm.py"), "--snap",a.model, "--tasks", tasks, "--limit", str(a.limit), "--data", a.data] if a.ram: cmd+=["--ram",str(a.ram)] e=env_for(a) - print(f" {C.dim}decode disk-bound: su hardware lento questo richiede ORE. Alza --limit su macchine veloci.{C.r}\n") + print(f" {C.dim}decode is disk-bound: this takes HOURS on slow hardware. Raise --limit on faster machines.{C.r}\n") sys.exit(subprocess.call(cmd, env=e)) def cmd_convert(a): @@ -499,34 +499,34 @@ def cmd_convert(a): "--repo", a.repo, "--outdir", a.model, "--ebits", str(a.ebits), "--io-bits", str(a.io_bits)] if a.xbits: base+=["--xbits",str(a.xbits)] # passo 1: modello principale (78 layer). Resumabile: riparte dagli shard mancanti. - print(f" {C.dim}[1/2] modello: {' '.join(base)}{C.r}") + print(f" {C.dim}[1/2] model: {' '.join(base)}{C.r}") rc=subprocess.call(base) if rc!=0: sys.exit(rc) if a.no_mtp: sys.exit(0) # passo 2: testa MTP (layer 78). SEMPRE int8: a int4 i draft sbagliano quasi sempre # (acceptance 0-4% vs 39-59%, misurato — issue #8) e la speculazione non parte mai. mtp_cmd=list(base); i=mtp_cmd.index("--ebits"); mtp_cmd[i+1]=str(max(8,a.ebits)) - print(f" {C.dim}[2/2] testa MTP a int8 (draft speculativi){C.r}") + print(f" {C.dim}[2/2] int8 MTP head (speculative drafts){C.r}") sys.exit(subprocess.call(mtp_cmd+["--mtp"])) def main(): common=argparse.ArgumentParser(add_help=False) common.add_argument("--model", default=DEF_MODEL); common.add_argument("--ram", type=int, default=0) # 0 = auto (il motore usa l'88% della RAM disponibile) - common.add_argument("--auto-tier",action="store_true",help="applica automaticamente il piano RAM/VRAM") + common.add_argument("--auto-tier",action="store_true",help="automatically apply the RAM/VRAM plan") common.add_argument("--ctx",type=int,default=0) - common.add_argument("--gpu",default=None,help="auto, none oppure lista device, es. 0,1") - common.add_argument("--vram",type=float,default=0,help="budget VRAM totale in GB (0=auto)") - common.add_argument("--repin", type=int, default=0, help="adatta gli expert RAM/VRAM ogni N token") + common.add_argument("--gpu",default=None,help="auto, none, or a device list such as 0,1") + common.add_argument("--vram",type=float,default=0,help="total VRAM budget in GB (0=auto)") + common.add_argument("--repin", type=int, default=0, help="adapt RAM/VRAM experts every N tokens") common.add_argument("--cap", type=int, default=8); common.add_argument("--ngen", type=int, default=1024) # rete di sicurezza: la fine vera la decidono gli stop token common.add_argument("--topp", type=float, default=0); common.add_argument("--topk", type=int, default=0) common.add_argument("--temp", type=float, default=None) # temperatura token (0=greedy, default 1.0+nucleus .95) - ap=argparse.ArgumentParser(prog="coli", parents=[common], description="colibrì — GLM-5.2 in locale") + ap=argparse.ArgumentParser(prog="coli", parents=[common], description="colibrì — run GLM-5.2 locally") sub=ap.add_subparsers(dest="cmd") sub.add_parser("build", parents=[common]); sub.add_parser("info", parents=[common]) pp=sub.add_parser("plan",parents=[common]) pp.add_argument("--json",action="store_true") pd=sub.add_parser("doctor",parents=[common]) - pd.add_argument("--json",action="store_true",help="emette un report JSON versionato") + pd.add_argument("--json",action="store_true",help="emit a versioned JSON report") pr=sub.add_parser("run", parents=[common]); pr.add_argument("prompt", nargs="*") sub.add_parser("chat", parents=[common]) ps=sub.add_parser("serve", parents=[common]) @@ -541,7 +541,7 @@ def main(): pb.add_argument("--limit",type=int,default=40); pb.add_argument("--data",default=os.path.join(HERE,"bench")) pc=sub.add_parser("convert", parents=[common]); pc.add_argument("--repo",default="zai-org/GLM-5.2-FP8") pc.add_argument("--ebits",type=int,default=4); pc.add_argument("--io-bits",type=int,default=8); pc.add_argument("--xbits",type=int,default=0) - pc.add_argument("--no-mtp",action="store_true",help="salta la testa MTP (niente draft speculativi)") + pc.add_argument("--no-mtp",action="store_true",help="skip the MTP head (no speculative drafts)") a=ap.parse_args() handler={"build":cmd_build,"info":cmd_info,"plan":cmd_plan,"doctor":cmd_doctor, "run":cmd_run,"chat":cmd_chat,"serve":cmd_serve,"bench":cmd_bench, diff --git a/c/glm.c b/c/glm.c index 6bd87c2..a6b73bb 100644 --- a/c/glm.c +++ b/c/glm.c @@ -161,10 +161,10 @@ static int qt_cuda_upload(QT *t){ } static void cuda_stats_print(void){ size_t n=0,b=0; coli_cuda_stats(-1,&n,&b); - fprintf(stderr,"[CUDA] resident set: %zu tensor, %.2f GB VRAM\n",n,b/1e9); + fprintf(stderr,"[CUDA] resident set: %zu tensors, %.2f GB VRAM\n",n,b/1e9); if(g_cuda_ndev>1) for(int i=0;i SIZE_MAX/sizeof(float)){ fprintf(stderr,"falloc: n=%lld fuori range\n",(long long)n); exit(1); } + if(n<0 || (uint64_t)n > SIZE_MAX/sizeof(float)){ fprintf(stderr,"falloc: n=%lld is out of range\n",(long long)n); exit(1); } float *p=malloc((size_t)n*sizeof(float)); if(!p){fprintf(stderr,"OOM\n");exit(1);} return p; } /* y[S,O] = x[S,I] @ W^T, W[O,I] f32 */ @@ -481,7 +481,7 @@ static void matmul_qt(float *y, const float *x, QT *w, int S){ : w->fmt==1 ? (const void*)w->q8 : (const void*)w->q4; if(coli_cuda_matmul(&w->cuda,y,x,weights,w->s,w->fmt,S,w->I,w->O,w->cuda_device)) return; w->cuda_failed=1; - fprintf(stderr,"[CUDA] tensor [%d,%d] su device %d disabilitato dopo errore; fallback CPU\n", + fprintf(stderr,"[CUDA] tensor [%d,%d] on device %d disabled after an error; falling back to CPU\n", w->O,w->I,w->cuda_device); } #endif @@ -681,11 +681,11 @@ static void load_cfg(Cfg *c, const char *snap){ } } c->qk_head=c->qk_nope+c->qk_rope; c->attn_scale = 1.f / sqrtf((float)c->qk_head); - if(c->n_group!=1){ fprintf(stderr,"questo motore assume n_group=1 (GLM-5.2)\n"); exit(1); } + if(c->n_group!=1){ fprintf(stderr,"this engine requires n_group=1 (GLM-5.2)\n"); exit(1); } /* VALIDAZIONE (report PR #25): il config.json arriva da mirror non fidati — dimensioni * ostili non devono superare questo punto. Un solo choke point protegge ogni alloc a valle. */ #define CKR(name,v,lo,hi) if((v)<(lo)||(v)>(hi)){ \ - fprintf(stderr,"config: %s=%d fuori range [%d,%d]\n",name,(int)(v),(int)(lo),(int)(hi)); exit(1); } + fprintf(stderr,"config: %s=%d is outside [%d,%d]\n",name,(int)(v),(int)(lo),(int)(hi)); exit(1); } CKR("hidden_size",c->hidden,1,1<<20) CKR("num_hidden_layers",c->n_layers,1,128) CKR("num_attention_heads",c->n_heads,1,1024) CKR("n_routed_experts",c->n_experts,1,4096) CKR("num_experts_per_tok",c->topk,1,64) CKR("moe_intermediate_size",c->moe_inter,1,1<<20) @@ -729,7 +729,7 @@ static QT qt_load(Model *m, const char *name, int O, int I, int bits){ return t; } static float *ld(Model *m, const char *name){ /* tensore 1D f32 residente (norme/bias) */ - int64_t n=st_numel(&m->S,name); if(n<0){fprintf(stderr,"manca %s\n",name);exit(1);} + int64_t n=st_numel(&m->S,name); if(n<0){fprintf(stderr,"missing %s\n",name);exit(1);} float *p=falloc(n); st_read_f32(&m->S,name,p,0); return p; } @@ -852,7 +852,7 @@ static void model_init(Model *m, const char *snap, int cap, int ebits, int dbits m->ix_knw[i]=ld(m,PI("k_norm.weight")); m->ix_knb[i]=ld(m,PI("k_norm.bias")); #undef PI } - fprintf(stderr,"[DSA] indexer attivo: attenzione sparsa top-%d oltre %d token di contesto\n", + fprintf(stderr,"[DSA] indexer active: top-%d sparse attention beyond %d context tokens\n", c->index_topk, c->index_topk); } } @@ -912,7 +912,7 @@ static void expert_load(Model *m, int layer, int eid, ESlot *s){ for(int k=0;k<3;k++){ tw[k]=st_find(&m->S,nm[k]); snprintf(qn,sizeof(qn),"%s.qs",nm[k]); tq[k]=st_find(&m->S,qn); - if(!tw[k]||!tq[k]){ fprintf(stderr,"manca %s\n",nm[k]); exit(1); } + if(!tw[k]||!tq[k]){ fprintf(stderr,"missing %s\n",nm[k]); exit(1); } } int64_t wtot=tw[0]->nbytes+tw[1]->nbytes+tw[2]->nbytes; int64_t ftot=(tq[0]->nbytes+tq[1]->nbytes+tq[2]->nbytes)/4; @@ -1548,21 +1548,21 @@ static inline int argmax_v(const float *lo, int V){ static void grammar_setup(Tok *T){ const char *gf=getenv("GRAMMAR"); if(!gf||!*gf) return; FILE *f=fopen(gf,"rb"); - if(!f){ fprintf(stderr,"[GRAMMAR] impossibile aprire %s\n",gf); return; } + if(!f){ fprintf(stderr,"[GRAMMAR] cannot open %s\n",gf); return; } fseek(f,0,SEEK_END); long n=ftell(f); fseek(f,0,SEEK_SET); char *txt=malloc((size_t)n+1); if(!txt || fread(txt,1,(size_t)n,f)!=(size_t)n){ - fprintf(stderr,"[GRAMMAR] lettura fallita: %s\n",gf); fclose(f); free(txt); return; } + fprintf(stderr,"[GRAMMAR] failed to read %s\n",gf); fclose(f); free(txt); return; } fclose(f); txt[n]=0; if(gr_parse(&g_gram,txt)){ fprintf(stderr,"[GRAMMAR] %s: %s\n",gf,g_gram.err); free(txt); return; } free(txt); gr_state_init(&g_gst,&g_gram); - if(!g_gst.alive){ fprintf(stderr,"[GRAMMAR] %s: grammatica non trattabile (ricorsione sinistra?)\n",gf); return; } + if(!g_gst.alive){ fprintf(stderr,"[GRAMMAR] %s: grammar cannot be evaluated (left recursion?)\n",gf); return; } if(getenv("GRAMMAR_DRAFT")) g_gr_max=atoi(getenv("GRAMMAR_DRAFT")); if(g_gr_max<1) g_gr_max=1; if(g_gr_max>48) g_gr_max=48; g_gr_T=T; g_gr_on=1; - fprintf(stderr,"[GRAMMAR] %s: %d regole, span forzato max %d token/forward\n",gf,g_gram.n,g_gr_max); + fprintf(stderr,"[GRAMMAR] %s: %d rules, forced span capped at %d tokens/forward\n",gf,g_gram.n,g_gr_max); } /* stato pulito all'inizio di ogni RISPOSTA (non tra i \x02MORE, che continuano) */ static void grammar_reset(void){ @@ -1592,7 +1592,7 @@ static int grammar_draft(int *draft, int cap){ if(g_gr_prop>=32 && g_gr_acc*2n_stop;i++) g_stop[g_nstop++]=c->stop_ids[i]; if(tok_eos>=0 && !is_stop(tok_eos)) g_stop[g_nstop++]=tok_eos; - fprintf(stderr,"[stop] %d token di stop:",g_nstop); + fprintf(stderr,"[stop] %d stop tokens:",g_nstop); for(int i=0;ihas_mtp && m->mtp_prop>=24 && m->mtp_acc*10 < m->mtp_prop){ g_draft=0; - fprintf(stderr,"[MTP] acceptance %.0f%% dopo %llu proposte: draft disattivati\n", + fprintf(stderr,"[MTP] %.0f%% acceptance after %llu proposals: drafts disabled\n", 100.0*m->mtp_acc/m->mtp_prop, (unsigned long long)m->mtp_prop); } } @@ -1703,7 +1703,7 @@ static int spec_decode(Model *m, int *all, int kv, int n_new, int eos, float *lo float *lo=step_all(m,batch,S,kv); m->n_fw++; int k=0; /* verifica: accetta finche' coincide */ if(g>0 && getenv("MTP_DEBUG")){ int veri=argmax_v(lo,V); - fprintf(stderr,"[mtpdbg] draft0=%d verita=%d %s\n", draft[0], veri, draft[0]==veri?"HIT":"miss"); } + fprintf(stderr,"[mtpdbg] draft0=%d verified=%d %s\n", draft[0], veri, draft[0]==veri?"HIT":"miss"); } while(kt_edisk+m->t_emm+m->t_attn+m->t_head; - printf("PROFILO: expert-disk %.3fs | expert-matmul %.3fs | attention %.3fs " - "(di cui kvb %.3fs) | lm_head %.3fs | altro %.3fs\n", + printf("PROFILE: expert-disk %.3fs | expert-matmul %.3fs | attention %.3fs " + "(including kvb %.3fs) | lm_head %.3fs | other %.3fs\n", m->t_edisk,m->t_emm,m->t_attn,m->t_kvb,m->t_head,elapsed-accounted); } @@ -1817,7 +1817,7 @@ static void profile_print(Model *m, double elapsed){ * replay the oracle sequence one token at a time. CPU and CUDA therefore see * identical hidden-state inputs even if their argmax predictions differ. */ static void run_replay(Model *m, const int *full, int nfull, int np){ - if(np<2||nfull<=np){ fprintf(stderr,"REPLAY richiede prompt e continuation non vuoti\n"); return; } + if(np<2||nfull<=np){ fprintf(stderr,"REPLAY requires a non-empty prompt and continuation\n"); return; } kv_alloc(m,nfull+2); float *logit=step(m,full,np-1,0); free(logit); m->hits=m->miss=m->ereq=m->gpu_expert_calls=0; @@ -1827,11 +1827,11 @@ static void run_replay(Model *m, const int *full, int nfull, int np){ logit=step(m,full+i,1,i); free(logit); steps++; } double dt=now_s()-t0, tot=m->hits+m->miss; - printf("REPLAY decode: %d token in %.3fs | %.2f tok/s | expert hit %.1f%%\n", + printf("REPLAY decode: %d tokens in %.3fs | %.2f tok/s | expert hit %.1f%%\n", steps,dt,steps/dt,tot?100.0*m->hits/tot:0.0); profile_print(m,dt); #ifdef COLI_CUDA - if(m->gpu_expert_count) printf("CUDA expert tier: %d residenti (%.2f GB) | %llu chiamate servite da VRAM\n", + if(m->gpu_expert_count) printf("CUDA expert tier: %d resident experts (%.2f GB) | %llu calls served from VRAM\n", m->gpu_expert_count,m->gpu_expert_bytes/1e9,(unsigned long long)m->gpu_expert_calls); if(g_cuda_enabled) cuda_stats_print(); #endif @@ -1849,8 +1849,8 @@ static void run_text(Model *m, const char *snap, const char *prompt, int ngen){ * distribuzione int4 e' rumore di quantizzazione */ int cap=(int)strlen(prompt)+16; int *pids=malloc(cap*sizeof(int)); int np=tok_encode(&T,prompt,(int)strlen(prompt),pids,cap); - if(np<1){ fprintf(stderr,"prompt vuoto dopo tokenizzazione\n"); return; } - printf("prompt: %d token | genero fino a %d (stop EOS=%d) | draft n-gram=%d\n", np, ngen, eos, g_draft); + if(np<1){ fprintf(stderr,"prompt is empty after tokenization\n"); return; } + printf("prompt: %d tokens | generating up to %d (EOS stop=%d) | n-gram draft=%d\n", np, ngen, eos, g_draft); fputs(prompt,stdout); fflush(stdout); kv_alloc(m, np+ngen+g_draft+2); int *all=malloc((np+ngen+g_draft+2)*sizeof(int)); memcpy(all,pids,np*sizeof(int)); @@ -1862,24 +1862,24 @@ static void run_text(Model *m, const char *snap, const char *prompt, int ngen){ double dt=now_s()-t; double tot=m->hits+m->miss; int nsp=0; for(int i=0;in_layers;i++) if(m->L[i].sparse) nsp++; - printf("\n---\n%d token in %.2fs (%.2f tok/s) | hit-rate expert %.1f%% | RSS %.2f GB\n", + printf("\n---\n%d tokens in %.2fs (%.2f tok/s) | expert hit rate %.1f%% | RSS %.2f GB\n", produced, dt, produced/dt, tot?100.0*m->hits/tot:0.0, rss_gb()); - printf("expert caricati/token: %.1f (per-layer %.2f su %d; baseline topk=%d) | TOPK=%d TOPP=%.2f\n", + printf("experts loaded/token: %.1f (per-layer %.2f across %d; baseline topk=%d) | TOPK=%d TOPP=%.2f\n", produced?(double)m->ereq/produced:0.0, (produced&&nsp)?(double)m->ereq/produced/nsp:0.0, nsp, c->topk, g_topk, g_topp); - printf("speculazione: %.2f token/forward (%llu fw per %llu tok) | MTP acceptance %.0f%% (%llu/%llu)\n", + printf("speculation: %.2f tokens/forward (%llu forwards per %llu tokens) | MTP acceptance %.0f%% (%llu/%llu)\n", m->n_fw?(double)m->n_emit/m->n_fw:1.0, (unsigned long long)m->n_fw, (unsigned long long)m->n_emit, m->mtp_prop?100.0*m->mtp_acc/m->mtp_prop:0.0, (unsigned long long)m->mtp_acc, (unsigned long long)m->mtp_prop); - if(g_gr_prop) printf("grammatica: acceptance %.0f%% (%llu/%llu draft forzati)\n", + if(g_gr_prop) printf("grammar: %.0f%% acceptance (%llu/%llu forced drafts)\n", 100.0*g_gr_acc/g_gr_prop, (unsigned long long)g_gr_acc, (unsigned long long)g_gr_prop); #ifdef COLI_CUDA - if(m->gpu_expert_count) printf("CUDA expert tier: %d residenti (%.2f GB) | %llu chiamate servite da VRAM\n", + if(m->gpu_expert_count) printf("CUDA expert tier: %d resident experts (%.2f GB) | %llu calls served from VRAM\n", m->gpu_expert_count,m->gpu_expert_bytes/1e9,(unsigned long long)m->gpu_expert_calls); if(g_cuda_enabled) cuda_stats_print(); #endif profile_print(m,dt); if(g_looka){ - const char *nm[3]={"token precedente (=SPEC prefetch)","ingresso layer, salto attention","layer successivo (1 giro di anticipo)"}; - printf("LOOKAHEAD routing — recall degli expert veri nel top-8 predetto:\n"); + const char *nm[3]={"previous token (=SPEC prefetch)","layer input, skip attention","next layer (one step ahead)"}; + printf("LOOKAHEAD routing — recall of true experts in predicted top-8:\n"); for(int i=0;i<3;i++) printf(" %-38s %5.1f%% (%lld/%lld)\n", nm[i], la_tot[i]?100.0*la_hit[i]/la_tot[i]:0.0, (long long)la_hit[i], (long long)la_tot[i]); } @@ -1950,11 +1950,11 @@ static void repin_pass(Model *m){ qt_cuda_reset(&s->g); qt_cuda_reset(&s->u); qt_cuda_reset(&s->d); s->g.cuda_eligible=s->u.cuda_eligible=s->d.cuda_eligible=0; m->gpu_expert_count--; m->gpu_expert_bytes-=old_gpu; - fprintf(stderr,"[REPIN] upload VRAM fallito; slot degradato a RAM\n"); + fprintf(stderr,"[REPIN] VRAM upload failed; slot downgraded to RAM\n"); } } #endif - fprintf(stderr,"[REPIN] %s layer %d: esce/out %d (heat=%u) <- entra/in %d (heat=%u) in %.0f ms\n", + fprintf(stderr,"[REPIN] %s layer %d: evict %d (heat=%u) <- admit %d (heat=%u) in %.0f ms\n", tier,cd[b].l,old,old_heat,cd[b].eid,new_heat,(now_s()-t0)*1e3); } for(int l=0;lc.n_layers;l++) if(m->eheat[l]) tier_decay(m->eheat[l],m->c.n_experts); @@ -2014,11 +2014,11 @@ static int kv_disk_load(Model *m, int *hist, int maxctx){ char mg[8]; int32_t h[8], w[8]; kv_hdr(m,w,0); if(fread(mg,1,8,f)!=8 || memcmp(mg,KV_MAGIC,8) || fread(h,4,8,f)!=8 || h[0]!=w[0]||h[1]!=w[1]||h[2]!=w[2]||h[3]!=w[3]||h[4]!=w[4]||h[5]!=w[5]){ - fprintf(stderr,"[KV] .coli_kv di un altro modello/versione: ignorato\n"); fclose(f); return 0; } + fprintf(stderr,"[KV] ignoring .coli_kv from a different model or version\n"); fclose(f); return 0; } int nrec=h[6]; if(nrec<1){ fclose(f); return 0; } if(nrec>=maxctx-8-g_draft){ - fprintf(stderr,"[KV] conversazione salvata (%d token) piu' grande del contesto: riparto da zero\n",nrec); + fprintf(stderr,"[KV] saved conversation (%d tokens) exceeds the context: starting over\n",nrec); fclose(f); return 0; } double t0=now_s(); for(int p=0;p0){ if(m->has_mtp) m->kv_start[c->n_layers]=-1; /* la finestra MTP riparte da sola */ - fprintf(stderr,"[KV] conversazione ripresa dal disco: %d token in %.1fs (niente re-prefill)\n", + fprintf(stderr,"[KV] resumed conversation from disk: %d tokens in %.1fs (no re-prefill)\n", nrec, now_s()-t0); } k->disk_nrec=nrec; @@ -2074,12 +2074,12 @@ static void run_serve(Model *m, const char *snap){ int templ=getenv("CHAT_TEMPLATE")?atoi(getenv("CHAT_TEMPLATE")):1; g_kvsave = getenv("KVSAVE")?atoi(getenv("KVSAVE")):1; int nctx=getenv("KV_SLOTS")?atoi(getenv("KV_SLOTS")):1; - if(nctx<1||nctx>16){ fprintf(stderr,"KV_SLOTS deve essere tra 1 e 16\n"); exit(2); } + if(nctx<1||nctx>16){ fprintf(stderr,"KV_SLOTS must be between 1 and 16\n"); exit(2); } KVState *initial=m->kv; free(initial->kv_start); free(initial); ServeCtx *ctx=calloc(nctx,sizeof(ServeCtx)); for(int i=0;ikv); - fprintf(stderr,"[KV] context slots: %d x %d token, projected pool %.2f GB\n", + fprintf(stderr,"[KV] context slots: %d x %d tokens, projected pool %.2f GB\n", nctx,maxctx,kv_pool_bytes(m,maxctx)/1e9); #define hist (sc->hist) #define len (sc->len) @@ -2232,7 +2232,7 @@ static void stats_dump_q(Model *m, const char *path, int quiet){ for(int i=0;i<=c->n_layers;i++){ if(!m->eusage[i]) continue; for(int e=0;en_experts;e++) if(m->eusage[i][e]){ fprintf(f,"%d %d %u\n",i,e,m->eusage[i][e]); tot+=m->eusage[i][e]; nz++; } } fclose(f); rename(tmp,path); - if(!quiet) fprintf(stderr,"[STATS] %lld selezioni su %lld expert distinti -> %s\n",(long long)tot,(long long)nz,path); + if(!quiet) fprintf(stderr,"[STATS] %lld selections across %lld distinct experts -> %s\n",(long long)tot,(long long)nz,path); } static void stats_dump(Model *m, const char *path){ stats_dump_q(m,path,0); } @@ -2290,11 +2290,11 @@ static void pin_wire(Model *m){ if(mem_wire(s->fslab, fl)==0) wired+=fl; else failed++; } } if(failed) - fprintf(stderr,"[PIN] mlock: %.1f GB inchiodati/wired, %ld alloc fallite/failed " - "(alza il limite / raise it: ulimit -l unlimited) in %.0fs\n", wired/1e9, failed, now_s()-t0); + fprintf(stderr,"[PIN] mlock: %.1f GB wired, %ld allocations failed " + "(raise the limit: ulimit -l unlimited) in %.0fs\n", wired/1e9, failed, now_s()-t0); else - fprintf(stderr,"[PIN] mlock: %.1f GB inchiodati in RAM fisica / wired in physical RAM " - "(niente compressione/no compression) in %.0fs\n", wired/1e9, now_s()-t0); + fprintf(stderr,"[PIN] mlock: %.1f GB wired in physical RAM " + "(no compression) in %.0fs\n", wired/1e9, now_s()-t0); } static void pin_load(Model *m, const char *statspath, double gb){ @@ -2329,7 +2329,7 @@ static void pin_load(Model *m, const char *statspath, double gb){ expert_load(m,li,r[a].e,&m->pin[li][slot]); } m->resident_bytes += (int64_t)npin*eb; - fprintf(stderr,"[PIN] hot-store: %d expert in RAM (%.1f GB) in %.0fs da %s\n", + fprintf(stderr,"[PIN] hot store: %d experts in RAM (%.1f GB) loaded in %.0fs from %s\n", npin, npin*eb/1e9, now_s()-t0, statspath); #ifdef COLI_CUDA if(g_cuda_enabled && g_cuda_expert_gb>0){ @@ -2378,9 +2378,9 @@ static void pin_load(Model *m, const char *statspath, double gb){ break; } } - fprintf(stderr,"[CUDA] hot expert tier: %d/%d expert, VRAM %.2f GB (budget totale %.1f GB)\n", + fprintf(stderr,"[CUDA] hot expert tier: %d/%d experts, VRAM %.2f GB (total budget %.1f GB)\n", m->gpu_expert_count,npin,m->gpu_expert_bytes/1e9,g_cuda_expert_gb); - for(int i=0;i0 && nsp>0) ? (int)(avail/((double)nsp*eb)) : 0; if(capmax<1) capmax=1; if(capmax < m->ecap){ - fprintf(stderr,"[RAM_GB=%.1f%s] residente %.1f GB + slack %.1f GB (ws %.1f, KV %dx%d %.1f, kvb %.1f), " - "expert %.1f MB x %d layer -> cap abbassato %d->%d (proiezione picco %.1f GB)\n", + fprintf(stderr,"[RAM_GB=%.1f%s] resident %.1f GB + reserve %.1f GB (ws %.1f, KV %dx%d %.1f, kvb %.1f), " + "experts %.1f MB x %d layers -> cap lowered %d->%d (projected peak %.1f GB)\n", ram_gb,auto_b?" auto":"",m->resident_bytes/1e9,slack/1e9,ws_b/1e9, kv_slot_count(),max_ctx,kv_b/1e9,kvb_b/1e9, eb/1e6, nsp, m->ecap, capmax, @@ -2482,13 +2482,13 @@ static void cap_for_ram(Model *m, double ram_gb, int ebits, int max_ctx){ m->ecache[i]=realloc(m->ecache[i],(size_t)newcap*sizeof(ESlot)); memset(m->ecache[i]+m->ecap,0,(size_t)(newcap-m->ecap)*sizeof(ESlot)); } - fprintf(stderr,"[RAM_GB=%.1f%s] cap ALZATO %d->%d: il budget lo consente " - "(proiezione picco %.1f GB; CAP_RAISE=0 per disattivare)\n", + fprintf(stderr,"[RAM_GB=%.1f%s] cap raised %d->%d: budget allows it " + "(projected peak %.1f GB; set CAP_RAISE=0 to disable)\n", ram_gb, auto_b?" auto":"", m->ecap, newcap, (m->resident_bytes + (double)newcap*nsp*eb + slack)/1e9); m->ecap=newcap; } else - fprintf(stderr,"[RAM_GB=%.1f%s] cap=%d ok (proiezione picco %.1f GB)\n", ram_gb, auto_b?" auto":"", m->ecap, + fprintf(stderr,"[RAM_GB=%.1f%s] cap=%d ok (projected peak %.1f GB)\n", ram_gb, auto_b?" auto":"", m->ecap, (m->resident_bytes + (double)m->ecap*nsp*eb + slack)/1e9); } } @@ -2523,47 +2523,47 @@ int main(int argc, char **argv){ int ebits= argc>2?atoi(argv[2]):8; int dbits= argc>3?atoi(argv[3]):ebits; if(getenv("SERVE") && (kv_slot_count()<1 || kv_slot_count()>16)){ - fprintf(stderr,"KV_SLOTS deve essere tra 1 e 16\n"); return 2; + fprintf(stderr,"KV_SLOTS must be between 1 and 16\n"); return 2; } #ifdef COLI_CUDA if(getenv("COLI_CUDA") && atoi(getenv("COLI_CUDA"))){ const char *one=getenv("COLI_GPU"), *many=getenv("COLI_GPUS"); - if(one&&many){ fprintf(stderr,"usa COLI_GPU oppure COLI_GPUS, non entrambi\n"); return 2; } + if(one&&many){ fprintf(stderr,"use COLI_GPU or COLI_GPUS, not both\n"); return 2; } if(many) g_cuda_ndev=parse_cuda_devices(many,g_cuda_devices); else if(one) g_cuda_ndev=parse_cuda_devices(one,g_cuda_devices); else { g_cuda_ndev=1; g_cuda_devices[0]=0; } - if(g_cuda_ndev<1){ fprintf(stderr,"COLI_GPUS non valido: usa una lista come 0,1,2\n"); return 2; } + if(g_cuda_ndev<1){ fprintf(stderr,"invalid COLI_GPUS: use a list such as 0,1,2\n"); return 2; } g_cuda_enabled=coli_cuda_init(g_cuda_devices,g_cuda_ndev); - if(!g_cuda_enabled){ fprintf(stderr,"[CUDA] backend richiesto ma non disponibile\n"); return 2; } + if(!g_cuda_enabled){ fprintf(stderr,"[CUDA] requested backend is unavailable\n"); return 2; } } g_cuda_dense=getenv("CUDA_DENSE")?atoi(getenv("CUDA_DENSE")):0; g_cuda_expert_gb=getenv("CUDA_EXPERT_GB")?atof(getenv("CUDA_EXPERT_GB")):0; - if((getenv("COLI_GPU")||getenv("COLI_GPUS"))&&!g_cuda_enabled){ fprintf(stderr,"COLI_GPU(S) richiede COLI_CUDA=1\n"); return 2; } - if(g_cuda_dense&&!g_cuda_enabled){ fprintf(stderr,"CUDA_DENSE richiede COLI_CUDA=1\n"); return 2; } - if(g_cuda_expert_gb>0 && !g_cuda_enabled){ fprintf(stderr,"CUDA_EXPERT_GB richiede COLI_CUDA=1\n"); return 2; } + if((getenv("COLI_GPU")||getenv("COLI_GPUS"))&&!g_cuda_enabled){ fprintf(stderr,"COLI_GPU(S) requires COLI_CUDA=1\n"); return 2; } + if(g_cuda_dense&&!g_cuda_enabled){ fprintf(stderr,"CUDA_DENSE requires COLI_CUDA=1\n"); return 2; } + if(g_cuda_expert_gb>0 && !g_cuda_enabled){ fprintf(stderr,"CUDA_EXPERT_GB requires COLI_CUDA=1\n"); return 2; } if(g_cuda_enabled) fprintf(stderr,"[CUDA] mode: routed experts%s\n",g_cuda_dense?" + resident dense tensors":" only (resident dense on CPU)"); #else if((getenv("COLI_CUDA") && atoi(getenv("COLI_CUDA"))) || getenv("COLI_GPU") || getenv("COLI_GPUS") || (getenv("CUDA_DENSE") && atoi(getenv("CUDA_DENSE"))) || (getenv("CUDA_EXPERT_GB") && atof(getenv("CUDA_EXPERT_GB"))>0)){ - fprintf(stderr,"CUDA richiesto ma questo binario e' CPU-only; ricompila con: make CUDA=1\n"); + fprintf(stderr,"CUDA was requested, but this binary is CPU-only; rebuild with: make CUDA=1\n"); return 2; } #endif - printf("== Motore C GLM (glm_moe_dsa), cache=%d expert/layer | expert@%d-bit densa@%d-bit | idot: " IDOT_KERNEL " ==\n", cap, ebits, dbits); + printf("== GLM C engine (glm_moe_dsa), cache=%d experts/layer | experts@%d-bit dense@%d-bit | idot: " IDOT_KERNEL " ==\n", cap, ebits, dbits); g_mem_avail_boot = mem_available_gb(); Model m; double t0=now_s(); model_init(&m,snap,cap,ebits,dbits); if(g_draft<0) g_draft = m.has_mtp ? 3 : 0; if(getenv("DSA_TOPK")) m.c.index_topk=atoi(getenv("DSA_TOPK")); /* override per test */ - printf("caricato in %.2fs | densa residente: %.2f MB | layers=%d experts=%d | MTP %s (draft=%d)\n", + printf("loaded in %.2fs | resident dense: %.2f MB | layers=%d experts=%d | MTP %s (draft=%d)\n", now_s()-t0, m.resident_bytes/(1024.0*1024.0), m.c.n_layers, m.c.n_experts, - m.has_mtp?"ATTIVA":"assente", g_draft); + m.has_mtp?"ACTIVE":"absent", g_draft); /* anche su stderr: e' il canale che le UI (coli) mostrano all'utente */ - fprintf(stderr,"[MTP] %s (draft=%d)\n", m.has_mtp?"attiva: decodifica speculativa nativa":"assente", g_draft); + fprintf(stderr,"[MTP] %s (draft=%d)\n", m.has_mtp?"active: native speculative decoding":"absent", g_draft); if(!strncmp(snap,"/mnt/",5)) - fprintf(stderr,"ATTENZIONE: il modello e' su %s (filesystem 9p/Windows, lento e fadvise inefficace).\n" - " Per RAM e velocita' tienilo su ext4 (es. /home/...).\n", snap); + fprintf(stderr,"WARNING: the model is on %s (slow 9p/Windows filesystem; fadvise is ineffective).\n" + " Keep it on ext4 (for example, /home/...) for memory efficiency and speed.\n", snap); /* HOT-STORE: PIN= [PIN_GB=g] -> top expert per frequenza fissi in RAM. * Va PRIMA di cap_for_ram: i pinnati contano nel residente. */ if(getenv("PIN")) pin_load(&m, getenv("PIN"), getenv("PIN_GB")?atof(getenv("PIN_GB")):10.0); @@ -2574,7 +2574,7 @@ int main(int argc, char **argv){ int est_ctx = getenv("CTX")?atoi(getenv("CTX")):4096; /* stesso default di run_serve */ snprintf(g_usage_path,sizeof(g_usage_path),"%s/.coli_usage",snap); int64_t hist = usage_load(&m,g_usage_path); - if(hist>0) fprintf(stderr,"[USAGE] storia expert: %lld selezioni (%s)\n",(long long)hist,g_usage_path); + if(hist>0) fprintf(stderr,"[USAGE] expert history: %lld selections (%s)\n",(long long)hist,g_usage_path); int autopin = getenv("AUTOPIN")?atoi(getenv("AUTOPIN")):1; if(!getenv("PIN") && autopin && hist>=5000){ /* quota pin proporzionale alla FIDUCIA nella storia: con pochi dati il pin @@ -2637,7 +2637,7 @@ int main(int argc, char **argv){ if(pred[i]==tf[i]) ok++; else fprintf(stderr,"[ORACLE] mismatch pos=%d expected=%d got=%d\n",i,tf[i],pred[i]); } - printf("PREFILL (teacher-forcing) C vs oracolo: %d/%d posizioni | %.1f pos/s\n", + printf("PREFILL (teacher-forcing) C vs oracle: %d/%d positions | %.1f pos/s\n", ok,nfull,nfull/tdt); if(okerr,sizeof G->err,"escape non valido nel letterale"); return -1; } } + if(b<0){ snprintf(G->err,sizeof G->err,"invalid escape in literal"); return -1; } } else b=(unsigned char)*p++; GrSym s; memset(&s,0,sizeof s); s.t=GR_CLS; s.c.bits[b>>3]|=(uint8_t)(1u<<(b&7)); - if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } } - if(*p!='"'){ snprintf(G->err,sizeof G->err,"letterale non chiuso"); return -1; } + if(*p!='"'){ snprintf(G->err,sizeof G->err,"unterminated literal"); return -1; } *pp=p+1; return 0; } static int gr__cls(Grammar *G, int ri, int ai, const char **pp){ @@ -139,22 +139,22 @@ static int gr__cls(Grammar *G, int ri, int ai, const char **pp){ while(*p && *p!=']'){ int lo, hi; if(*p=='\\'){ p++; lo=gr__esc(&p); - if(lo<0){ snprintf(G->err,sizeof G->err,"escape non valido nella classe"); return -1; } } + if(lo<0){ snprintf(G->err,sizeof G->err,"invalid escape in character class"); return -1; } } else lo=(unsigned char)*p++; hi=lo; if(*p=='-' && p[1] && p[1]!=']'){ p++; if(*p=='\\'){ p++; hi=gr__esc(&p); - if(hi<0){ snprintf(G->err,sizeof G->err,"escape non valido nella classe"); return -1; } } + if(hi<0){ snprintf(G->err,sizeof G->err,"invalid escape in character class"); return -1; } } else hi=(unsigned char)*p++; } if(hi>3]|=(uint8_t)(1u<<(b&7)); } - if(*p!=']'){ snprintf(G->err,sizeof G->err,"classe non chiusa"); return -1; } + if(*p!=']'){ snprintf(G->err,sizeof G->err,"unterminated character class"); return -1; } if(neg) for(int i=0;i<32;i++) s.c.bits[i]=(uint8_t)~s.c.bits[i]; *pp=p+1; - if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } return 0; } /* postfisso ? * + sull'ITEM appena letto (simboli [n0, n) dell'alternate corrente). @@ -179,28 +179,28 @@ static int gr__postfix(Grammar *G, int ri, int ai, int n0, char op){ if(gr__push(G,ri,ai,&R)) goto full; /* l'item nell'alternate diventa R */ return 0; full: - snprintf(G->err,sizeof G->err,"grammatica troppo grande"); + snprintf(G->err,sizeof G->err,"grammar is too large"); return -1; } static int gr__alts(Grammar *G, int ri, const char **pp, int depth, int in_group){ - if(depth>32){ snprintf(G->err,sizeof G->err,"gruppi troppo annidati"); return -1; } + if(depth>32){ snprintf(G->err,sizeof G->err,"groups are nested too deeply"); return -1; } const char *p=*pp; int ai=gr__alt_new(G,ri); - if(ai<0){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(ai<0){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } for(;;){ p=gr__ws(p); if(!*p){ - if(in_group){ snprintf(G->err,sizeof G->err,"manca ')'"); return -1; } + if(in_group){ snprintf(G->err,sizeof G->err,"missing ')'"); return -1; } break; } if(*p==')'){ - if(!in_group){ snprintf(G->err,sizeof G->err,"')' inatteso"); return -1; } + if(!in_group){ snprintf(G->err,sizeof G->err,"unexpected ')'"); return -1; } break; } if(*p=='|'){ p++; ai=gr__alt_new(G,ri); - if(ai<0){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(ai<0){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } continue; } int n0=G->r[ri].a[ai].n; @@ -211,24 +211,24 @@ static int gr__alts(Grammar *G, int ri, const char **pp, int depth, int in_group } else if(*p=='('){ p++; int gi=gr__anon(G); - if(gi<0){ snprintf(G->err,sizeof G->err,"grammatica troppo grande"); return -1; } + if(gi<0){ snprintf(G->err,sizeof G->err,"grammar is too large"); return -1; } if(gr__alts(G,gi,&p,depth+1,1)) return -1; p=gr__ws(p); - if(*p!=')'){ snprintf(G->err,sizeof G->err,"manca ')'"); return -1; } + if(*p!=')'){ snprintf(G->err,sizeof G->err,"missing ')'"); return -1; } p++; GrSym s; memset(&s,0,sizeof s); s.t=GR_REF; s.ref=(int16_t)gi; - if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } } else if(gr__idch(*p)){ int nl=gr__idlen(p); const char *after=gr__ws(p+nl); if(!in_group && !strncmp(after,"::=",3)) break; /* inizia la prossima regola */ int ref=gr__rule(G,p,nl); - if(ref<0){ snprintf(G->err,sizeof G->err,"troppe regole"); return -1; } + if(ref<0){ snprintf(G->err,sizeof G->err,"too many rules"); return -1; } p+=nl; GrSym s; memset(&s,0,sizeof s); s.t=GR_REF; s.ref=(int16_t)ref; - if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"memoria esaurita"); return -1; } + if(gr__push(G,ri,ai,&s)){ snprintf(G->err,sizeof G->err,"out of memory"); return -1; } } else { - snprintf(G->err,sizeof G->err,"carattere inatteso '%c'",*p); return -1; + snprintf(G->err,sizeof G->err,"unexpected character '%c'",*p); return -1; } p=gr__ws(p); if(*p=='?'||*p=='*'||*p=='+'){ if(gr__postfix(G,ri,ai,n0,*p)) return -1; p++; } @@ -244,21 +244,21 @@ static int gr_parse(Grammar *G, const char *src){ p=gr__ws(p); if(!*p) break; int nl=gr__idlen(p); - if(nl<=0){ snprintf(G->err,sizeof G->err,"attesa una regola, trovato '%c'",*p); return -1; } + if(nl<=0){ snprintf(G->err,sizeof G->err,"expected a rule, found '%c'",*p); return -1; } const char *name=p; const char *q=gr__ws(p+nl); - if(strncmp(q,"::=",3)){ snprintf(G->err,sizeof G->err,"atteso '::=' dopo '%.*s'",nl,name); return -1; } + if(strncmp(q,"::=",3)){ snprintf(G->err,sizeof G->err,"expected '::=' after '%.*s'",nl,name); return -1; } p=q+3; int ri=gr__rule(G,name,nl); - if(ri<0){ snprintf(G->err,sizeof G->err,"troppe regole"); return -1; } - if(G->r[ri].n>0){ snprintf(G->err,sizeof G->err,"regola '%.*s' duplicata",nl,name); return -1; } + if(ri<0){ snprintf(G->err,sizeof G->err,"too many rules"); return -1; } + if(G->r[ri].n>0){ snprintf(G->err,sizeof G->err,"duplicate rule '%.*s'",nl,name); return -1; } if(gr__alts(G,ri,&p,0,0)) return -1; } for(int i=0;in;i++){ if(!strcmp(G->r[i].name,"root")) G->root=i; - if(G->r[i].n==0){ snprintf(G->err,sizeof G->err,"regola '%s' usata ma mai definita",G->r[i].name); return -1; } + if(G->r[i].n==0){ snprintf(G->err,sizeof G->err,"rule '%s' is used but never defined",G->r[i].name); return -1; } } - if(G->root<0){ snprintf(G->err,sizeof G->err,"manca la regola 'root'"); return -1; } + if(G->root<0){ snprintf(G->err,sizeof G->err,"missing 'root' rule"); return -1; } return 0; } static void gr_free(Grammar *G){ diff --git a/c/iobench.c b/c/iobench.c index d2b2374..fdc94cb 100644 --- a/c/iobench.c +++ b/c/iobench.c @@ -18,26 +18,26 @@ #endif static double now(){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec+t.tv_nsec*1e-9; } int main(int argc,char**argv){ - if(argc<2){fprintf(stderr,"uso: %s file [blkMB] [n] [threads] [direct 0/1]\n",argv[0]);return 1;} + if(argc<2){fprintf(stderr,"usage: %s file [blkMB] [n] [threads] [direct 0/1]\n",argv[0]);return 1;} long blk=(argc>2?atol(argv[2]):19)*1024*1024; int n=argc>3?atoi(argv[3]):64; int nth=argc>4?atoi(argv[4]):8; int direct=argc>5?atoi(argv[5]):1; #ifdef O_DIRECT int fd=open(argv[1],O_RDONLY|(direct?O_DIRECT:0)); - if(fd<0 && direct){ fprintf(stderr,"O_DIRECT non disponibile (%s), uso buffered\n",strerror(errno)); + if(fd<0 && direct){ fprintf(stderr,"O_DIRECT is unavailable (%s); using buffered I/O\n",strerror(errno)); direct=0; fd=open(argv[1],O_RDONLY); } #else int fd=open(argv[1],O_RDONLY); /* macOS: F_NOCACHE ~ O_DIRECT */ #ifdef __APPLE__ if(direct && fd>=0) fcntl(fd,F_NOCACHE,1); #else - if(direct){ fprintf(stderr,"O_DIRECT non disponibile, uso buffered\n"); direct=0; } + if(direct){ fprintf(stderr,"O_DIRECT is unavailable; using buffered I/O\n"); direct=0; } #endif #endif if(fd<0){perror("open");return 1;} off_t sz=lseek(fd,0,SEEK_END); - if(sz %.2f GB/s (%.1f ms/blocco effettivi)\n", + printf("%s x%d threads: %d reads x %ldMB = %.1f GB in %.2fs -> %.2f GB/s (%.1f effective ms/block)\n", direct?"O_DIRECT":"buffered", nth, n, blk/1024/1024, tot/1e9, dt, tot/1e9/dt, dt/n*1000); close(fd); free(offs); return 0; } diff --git a/c/olmoe.c b/c/olmoe.c index 84b9f0a..641f1d9 100644 --- a/c/olmoe.c +++ b/c/olmoe.c @@ -137,7 +137,7 @@ static void load_cfg(Cfg *c, const char *snap) { static float *load_t(Model *m, const char *name) { int64_t n = st_numel(&m->S, name); - if (n < 0) { fprintf(stderr, "manca %s\n", name); exit(1); } + if (n < 0) { fprintf(stderr, "missing %s\n", name); exit(1); } float *p = falloc(n); st_read_f32(&m->S, name, p, 0); /* densa: niente DONTNEED, resta residente */ return p; @@ -357,7 +357,7 @@ static int *read_int_array(jval *o, const char *key, int *n_out) { int main(int argc, char **argv) { const char *snap = getenv("SNAP"); - if (!snap) { fprintf(stderr, "imposta SNAP=\n"); return 1; } + if (!snap) { fprintf(stderr, "set SNAP=\n"); return 1; } int cap = argc > 1 ? atoi(argv[1]) : 16; int bits = argc > 2 ? atoi(argv[2]) : 8; const char *refpath = argc > 3 ? argv[3] : "ref.json"; @@ -369,9 +369,9 @@ int main(int argc, char **argv) { int np, nfull; int *prompt = read_int_array(ref,"prompt_ids",&np); int *full = read_int_array(ref,"full_ids",&nfull); int n_new = nfull - np; - printf("== Motore C streaming, cache = %d expert/layer, expert @ %d-bit ==\n", cap, bits); + printf("== Streaming C engine, cache = %d experts/layer, experts @ %d-bit ==\n", cap, bits); Model m; model_init(&m, snap, cap, bits); - printf("densa caricata in %.1fs | RSS dopo load densa: %.2f GB\n", m.dense_load_s, rss_gb()); + printf("resident weights loaded in %.1fs | RSS after load: %.2f GB\n", m.dense_load_s, rss_gb()); int *out = malloc((np + n_new) * sizeof(int)); double t = now_s(); @@ -379,14 +379,14 @@ int main(int argc, char **argv) { double dt = now_s() - t; int match = 0; - printf("\nRiferimento: "); for (int i=np;i/dev/null 2>&1; do - echo "[1/4] attendo lo spostamento su ext4... ($(du -sh "$DIR" 2>/dev/null | cut -f1))"; sleep 20 + echo "[1/4] waiting for the move to ext4... ($(du -sh "$DIR" 2>/dev/null | cut -f1))"; sleep 20 done -echo "[1/4] spostamento completato: $(du -sh "$DIR" | cut -f1), shard $(ls "$DIR"/*.safetensors 2>/dev/null | wc -l)" +echo "[1/4] move complete: $(du -sh "$DIR" | cut -f1), shards $(ls "$DIR"/*.safetensors 2>/dev/null | wc -l)" # 2) riprende+completa la conversione (ripartibile: salta gli shard gia' fatti) -echo "[2/4] conversione (riprende da dove era): output -> $DIR" +echo "[2/4] conversion (resumes where it stopped): output -> $DIR" python3 tools/convert_fp8_to_int4.py --repo "$REPO" --outdir "$DIR" --ebits 4 --io-bits 8 # 3) il motore richiede tokenizer.json + config.json nella dir del modello for f in config.json tokenizer.json; do - [ -f "$DIR/$f" ] || { echo "ERRORE: manca $DIR/$f"; exit 1; } + [ -f "$DIR/$f" ] || { echo "ERROR: missing $DIR/$f"; exit 1; } done -echo "[3/4] compilo il motore"; make -s glm +echo "[3/4] building the engine"; make -s glm # 4) generazione reale, con auto-cap dal budget RAM e heartbeat RSS su stderr -echo "[4/4] genero (RAM_GB=$RAM_GB, NGEN=$NGEN)"; echo "------" +echo "[4/4] generating (RAM_GB=$RAM_GB, NGEN=$NGEN)"; echo "------" SNAP="$DIR" RAM_GB="$RAM_GB" PROMPT="$PROMPT" NGEN="$NGEN" ./glm 64 diff --git a/c/scripts/supervisor.sh b/c/scripts/supervisor.sh index bba4184..a180eed 100755 --- a/c/scripts/supervisor.sh +++ b/c/scripts/supervisor.sh @@ -13,7 +13,7 @@ STALL_S=180 # secondi senza crescita del download -> riavvio CONVLOG=/tmp/convert_supervised.log exec 9>"$DIR/.supervisor.lock" -flock -n 9 || { echo "supervisore gia' attivo, esco"; exit 1; } +flock -n 9 || { echo "a supervisor is already running; exiting"; exit 1; } log(){ echo "[$(date +%H:%M:%S)] $*"; } @@ -21,16 +21,16 @@ start_conv(){ cd "$CODE" nohup python3 tools/convert_fp8_to_int4.py --repo zai-org/GLM-5.2-FP8 \ --outdir "$DIR" --ebits 4 --io-bits 8 >> "$CONVLOG" 2>&1 & - log "convertitore avviato (PID $!)" + log "converter started (PID $!)" } last_size=-1; stall=0 while :; do done_n=$(ls "$DIR"/out-*.safetensors 2>/dev/null | wc -l) - if [ "$done_n" -ge "$TOTAL" ]; then log "FATTO: $done_n/$TOTAL shard. Esco."; pkill -f convert_fp8 2>/dev/null; exit 0; fi + if [ "$done_n" -ge "$TOTAL" ]; then log "DONE: $done_n/$TOTAL shards. Exiting."; pkill -f convert_fp8 2>/dev/null; exit 0; fi if ! pgrep -f convert_fp8 >/dev/null; then - log "convertitore non attivo ($done_n/$TOTAL): lo avvio" + log "converter is not running ($done_n/$TOTAL): starting it" start_conv; last_size=-1; stall=0; sleep 20; continue fi @@ -40,12 +40,12 @@ while :; do if [ "$size" = "$last_size" ]; then stall=$((stall+30)) if [ "$stall" -ge "$STALL_S" ]; then - log "download FERMO da ${stall}s a $((size/1000000)) MB ($done_n/$TOTAL): riavvio il convertitore" + log "download stalled for ${stall}s at $((size/1000000)) MB ($done_n/$TOTAL): restarting the converter" pkill -f convert_fp8; sleep 5 start_conv; last_size=-1; stall=0 fi else - [ "$last_size" -ge 0 ] && [ "$stall" -ge 60 ] && log "download ripreso ($((size/1000000)) MB)" + [ "$last_size" -ge 0 ] && [ "$stall" -ge 60 ] && log "download resumed ($((size/1000000)) MB)" last_size=$size; stall=0 fi else diff --git a/c/setup.sh b/c/setup.sh index c220bd1..e31e60a 100755 --- a/c/setup.sh +++ b/c/setup.sh @@ -9,35 +9,35 @@ echo "🐦 colibrì — setup" UNAME_S=$(uname -s) # 1) dipendenze -command -v make >/dev/null || { echo "manca make"; exit 1; } +command -v make >/dev/null || { echo "make is missing"; exit 1; } case "$UNAME_S" in Darwin) - command -v clang >/dev/null || { echo "manca clang (xcode-select --install)"; exit 1; } + command -v clang >/dev/null || { echo "clang is missing (run: xcode-select --install)"; exit 1; } echo " clang: $(clang --version | head -1) · $(sysctl -n hw.ncpu) core" echo -n " OpenMP: " if [ -f "$(brew --prefix libomp 2>/dev/null)/lib/libomp.dylib" ]; then echo "ok (libomp)" - else echo "libomp assente -> build single-thread (consigliato: brew install libomp)"; fi + else echo "libomp is missing -> single-threaded build (recommended: brew install libomp)"; fi ;; MINGW*|MSYS*) - command -v gcc >/dev/null || { echo "manca gcc (MinGW-w64). Installa: pacman -S mingw-w64-x86_64-gcc make"; exit 1; } + command -v gcc >/dev/null || { echo "gcc is missing (MinGW-w64). Install: pacman -S mingw-w64-x86_64-gcc make"; exit 1; } echo " gcc: $(gcc -dumpversion) · MinGW-w64" - echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "manca libgomp (pacman -S mingw-w64-x86_64-gcc)"; exit 1; } + echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "libgomp is missing (pacman -S mingw-w64-x86_64-gcc)"; exit 1; } ;; *) - command -v gcc >/dev/null || { echo "manca gcc (es: sudo apt install build-essential)"; exit 1; } + command -v gcc >/dev/null || { echo "gcc is missing (for example: sudo apt install build-essential)"; exit 1; } echo " gcc: $(gcc -dumpversion) · $(nproc) core" - echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "manca (libgomp)"; exit 1; } + echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "libgomp is missing"; exit 1; } ;; esac # 2) build: nativa (veloce, per QUESTA macchina). Per un binario da distribuire: make portable -echo " compilo (ARCH=${ARCH:-native})…" +echo " building (ARCH=${ARCH:-native})…" make -s glm ARCH="${ARCH:-native}" # 3) self-test sull'oracolo tiny, se presente if [ -d glm_tiny ] && [ -f ref_glm.json ]; then - r=$(SNAP=./glm_tiny TF=1 ./glm 64 16 16 2>/dev/null | grep -oE "[0-9]+/[0-9]+ posizioni" || true) - echo " self-test motore: ${r:-?} (atteso 32/32)" + r=$(SNAP=./glm_tiny TF=1 ./glm 64 16 16 2>/dev/null | grep -oE "[0-9]+/[0-9]+ positions" || true) + echo " engine self-test: ${r:-?} (expected 32/32)" fi # 4) info macchina (la velocità dipende da QUESTI due numeri, non dalla GPU) @@ -53,12 +53,12 @@ MINGW*|MSYS*) ram=$(awk '/MemTotal/{printf "%.0f", $2/1e6}' /proc/meminfo 2>/dev/null || echo "?") ;; esac -echo " RAM: ${ram} GB (più RAM = più expert in cache = più veloce)" +echo " RAM: ${ram} GB (more RAM = more cached experts = faster inference)" echo -echo "pronto. Prossimi passi:" -echo " ./coli build # (gia' fatto)" -echo " ./coli convert --model /percorso/NVMe/glm52_i4 # genera il modello int4 (ore)" -echo " ./coli info --model /percorso/NVMe/glm52_i4" -echo " ./coli chat --model /percorso/NVMe/glm52_i4 --ram " +echo "ready. Next steps:" +echo " ./coli build # already done" +echo " ./coli convert --model /path/on/NVMe/glm52_i4 # generate the int4 model (hours)" +echo " ./coli info --model /path/on/NVMe/glm52_i4" +echo " ./coli chat --model /path/on/NVMe/glm52_i4 --ram " echo -echo "IMPORTANTE: tieni il modello su disco VELOCE (NVMe/ext4), MAI su /mnt/c o rete." +echo "IMPORTANT: keep the model on fast storage (NVMe/ext4), never on /mnt/c or a network mount." diff --git a/c/st.h b/c/st.h index 69ed9f1..11de920 100644 --- a/c/st.h +++ b/c/st.h @@ -52,7 +52,7 @@ static int st_dtype_code(const char *s) { if (!strcmp(s, "F32")) return 2; if (!strcmp(s, "U8")) return 3; /* dati quantizzati (int4 packed / int8) */ if (!strcmp(s, "I8")) return 3; - fprintf(stderr, "dtype non gestito: %s\n", s); exit(1); + fprintf(stderr, "unsupported dtype: %s\n", s); exit(1); } static inline float bf16_to_f32(uint16_t h) { @@ -108,7 +108,7 @@ static void st_init(shards *S, const char *snap_dir) { while ((e = readdir(d))) { const char *dot = strrchr(e->d_name, '.'); if (dot && !strcmp(dot, ".safetensors")) { /* model.safetensors o model-0000N-of-... */ - if (nf >= ST_MAX_SHARDS) { fprintf(stderr, "troppi shard (>%d): alza ST_MAX_SHARDS\n", ST_MAX_SHARDS); exit(1); } + if (nf >= ST_MAX_SHARDS) { fprintf(stderr, "too many shards (>%d): raise ST_MAX_SHARDS\n", ST_MAX_SHARDS); exit(1); } snprintf(files[nf++], 1024, "%s/%s", snap_dir, e->d_name); } } @@ -182,7 +182,7 @@ static void st_prefetch(shards *S, const char *name) { * drop=1 -> consiglia al kernel di scartare le pagine (per gli expert in streaming). */ static int64_t st_read_f32(shards *S, const char *name, float *out, int drop) { st_tensor *t = st_find(S, name); - if (!t) { fprintf(stderr, "tensore mancante: %s\n", name); exit(1); } + if (!t) { fprintf(stderr, "missing tensor: %s\n", name); exit(1); } void *raw = malloc(t->nbytes); if (pread(t->fd, raw, t->nbytes, t->off) != t->nbytes) { perror("pread data"); exit(1); } if (t->dtype == 2) { @@ -208,7 +208,7 @@ static int64_t st_nbytes(shards *S, const char *name) { * quantizzati int4/int8 del nostro container (dtype U8). drop=1 -> fadvise DONTNEED. */ static void st_read_raw(shards *S, const char *name, void *out, int drop) { st_tensor *t = st_find(S, name); - if (!t) { fprintf(stderr, "tensore mancante: %s\n", name); exit(1); } + if (!t) { fprintf(stderr, "missing tensor: %s\n", name); exit(1); } if (pread(t->fd, out, t->nbytes, t->off) != t->nbytes) { perror("pread raw"); exit(1); } if (drop) posix_fadvise(t->fd, t->off, t->nbytes, POSIX_FADV_DONTNEED); } @@ -218,7 +218,7 @@ static void st_read_raw(shards *S, const char *name, void *out, int drop) { * solo expert richiesto via pread del sotto-range, niente lettura dell'intero blocco. */ static void st_read_slice_f32(shards *S, const char *name, int64_t elem_off, int64_t n_elems, float *out, int drop) { st_tensor *t = st_find(S, name); - if (!t) { fprintf(stderr, "tensore mancante: %s\n", name); exit(1); } + if (!t) { fprintf(stderr, "missing tensor: %s\n", name); exit(1); } int esz = (t->dtype == 2) ? 4 : 2; int64_t boff = t->off + elem_off * esz, nb = n_elems * esz; void *raw = malloc(nb); diff --git a/c/tests/test_benchmark_cuda_fixture.py b/c/tests/test_benchmark_cuda_fixture.py index 542a7da..a704052 100644 --- a/c/tests/test_benchmark_cuda_fixture.py +++ b/c/tests/test_benchmark_cuda_fixture.py @@ -5,7 +5,7 @@ from tools.benchmark_cuda_fixture import parse_output SAMPLE = """ REPLAY decode: 4 tokens | 12.34 tok/s -PROFILO: expert-disk 1.25s | expert-matmul 2.50s | attention 0.75s | lm_head 0.10s | altro -0.05s +PROFILE: expert-disk 1.25s | expert-matmul 2.50s | attention 0.75s | lm_head 0.10s | other -0.05s """ diff --git a/c/tests/test_cli_output.py b/c/tests/test_cli_output.py new file mode 100644 index 0000000..77fc86a --- /dev/null +++ b/c/tests/test_cli_output.py @@ -0,0 +1,49 @@ +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + + +HERE = Path(__file__).resolve().parent.parent +CLI = HERE / "coli" + + +class CliOutputLanguageTest(unittest.TestCase): + def run_cli(self, *args): + return subprocess.run( + [sys.executable, str(CLI), *args], + cwd=HERE, + text=True, + capture_output=True, + check=False, + timeout=10, + ) + + def test_help_is_english(self): + result = self.run_cli("--help") + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("run GLM-5.2 locally", result.stdout) + self.assertIn("automatically apply the RAM/VRAM plan", result.stdout) + self.assertNotIn("modello", result.stdout.lower()) + self.assertNotIn("motore", result.stdout.lower()) + + def test_info_status_is_english(self): + with tempfile.TemporaryDirectory() as model: + result = self.run_cli("info", "--model", model) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertIn("config.json is missing", result.stdout) + self.assertIn("disk", result.stdout) + self.assertIn("engine", result.stdout) + + def test_missing_model_error_is_english(self): + with tempfile.TemporaryDirectory() as directory: + missing_model = str(Path(directory) / "missing-model") + result = self.run_cli("run", "--model", missing_model, "hello") + self.assertNotEqual(result.returncode, 0) + self.assertIn("model not found", result.stderr) + self.assertIn("set COLI_MODEL or use --model", result.stderr) + + +if __name__ == "__main__": + unittest.main() diff --git a/c/tests/test_tok.c b/c/tests/test_tok.c index 10562f8..cceb2f6 100644 --- a/c/tests/test_tok.c +++ b/c/tests/test_tok.c @@ -5,10 +5,10 @@ #include "../tok.h" int main(int argc, char **argv){ - if(argc<2){ fprintf(stderr,"uso: %s tokenizer.json < casi\n",argv[0]); return 1; } + if(argc<2){ fprintf(stderr,"usage: %s tokenizer.json < cases\n",argv[0]); return 1; } Tok T; tok_load(&T, argv[1]); - fprintf(stderr,"caricato: vocab_ids=%d specials=%d\n", T.n_ids, T.nsp); + fprintf(stderr,"loaded: vocab_ids=%d specials=%d\n", T.n_ids, T.nsp); char *line=NULL; size_t cap=0; ssize_t nr; int pass=0, tot=0, dpass=0; while((nr=getline(&line,&cap,stdin))>=0){ diff --git a/c/tok.h b/c/tok.h index e5ec6f8..f61011e 100644 --- a/c/tok.h +++ b/c/tok.h @@ -97,7 +97,7 @@ static void tok_load(Tok *T, const char *path){ jval *vocab=json_get(model,"vocab"); jval *merges=json_get(model,"merges"); jval *added=json_get(root,"added_tokens"); - if(!vocab||!merges){ fprintf(stderr,"tokenizer.json: manca model.vocab/merges\n"); exit(1); } + if(!vocab||!merges){ fprintf(stderr,"tokenizer.json: missing model.vocab/merges\n"); exit(1); } /* id massimo per dimensionare id2str */ int maxid=0; diff --git a/c/tok_unicode.h b/c/tok_unicode.h index 62959de..e6febb8 100644 --- a/c/tok_unicode.h +++ b/c/tok_unicode.h @@ -1,4 +1,4 @@ -/* GENERATO da tools/gen_unicode.py — non modificare a mano. */ +/* GENERATED by tools/gen_unicode.py — do not edit by hand. */ #ifndef TOK_UNICODE_H #define TOK_UNICODE_H #include diff --git a/c/tools/benchmark_cuda_fixture.py b/c/tools/benchmark_cuda_fixture.py index 7352ed4..1b44205 100644 --- a/c/tools/benchmark_cuda_fixture.py +++ b/c/tools/benchmark_cuda_fixture.py @@ -11,8 +11,8 @@ from pathlib import Path SPEED_RE = re.compile(r"REPLAY decode:.*\| ([0-9.]+) tok/s") PROFILE_RE = re.compile( - r"PROFILO: expert-disk ([0-9.]+)s \| expert-matmul ([0-9.]+)s " - r"\| attention ([0-9.]+)s .* lm_head ([0-9.]+)s \| altro ([0-9.-]+)s" + r"PROFILE: expert-disk ([0-9.]+)s \| expert-matmul ([0-9.]+)s " + r"\| attention ([0-9.]+)s .* lm_head ([0-9.]+)s \| other ([0-9.-]+)s" ) PROFILE_KEYS = ("disk", "expert_matmul", "attention", "lm_head", "other") diff --git a/c/tools/convert_fp8_to_int4.py b/c/tools/convert_fp8_to_int4.py index 4ba1812..84f6d19 100644 --- a/c/tools/convert_fp8_to_int4.py +++ b/c/tools/convert_fp8_to_int4.py @@ -139,11 +139,12 @@ def main(): ap.add_argument("--min-free-gb", type=float, default=20.0) ap.add_argument("--selftest", action="store_true") ap.add_argument("--mtp", action="store_true", - help="scarica/converte SOLO la testa MTP (model.layers..*) -> out-mtp-*.safetensors") + help="download and convert ONLY the MTP head (model.layers..*) -> out-mtp-*.safetensors") ap.add_argument("--indexer", action="store_true", - help="estrae SOLO i pesi del DSA lightning indexer -> out-idx-*.safetensors. ATTENZIONE: " - "i tensori indexer sono sparsi su ~tutti gli shard: ri-scarica l'intero repo (~756 GB " - "di traffico) per tenerne pochi GB. Resumabile shard per shard. Consigliato --ebits 8.") + help="extract ONLY the DSA lightning-indexer weights -> out-idx-*.safetensors. WARNING: " + "indexer tensors are spread across nearly every shard, so this re-downloads the whole " + "repository (~756 GB of traffic) to retain only a few GB. Resumable per shard. " + "Recommended: --ebits 8.") a = ap.parse_args() if a.ebits is None: # testa MTP a int4 = acceptance ~0-4% (misurato, issue #8): il draft sbaglia sempre @@ -163,8 +164,8 @@ def main(): q = (w / sc.repeat_interleave(bs,0).repeat_interleave(bs,1)).to(torch.float8_e4m3fn) deq = (q.to(torch.float32) * sc.repeat_interleave(bs,0).repeat_interleave(bs,1)) rel = (deq - w).abs().mean() / w.abs().mean() - print(f"[selftest fp8 block-dequant] errore relativo medio = {rel:.4f} " - f"({'OK' if rel < 0.05 else 'ALTO'})") + print(f"[selftest fp8 block-dequant] mean relative error = {rel:.4f} " + f"({'OK' if rel < 0.05 else 'HIGH'})") return os.makedirs(a.outdir, exist_ok=True) @@ -178,7 +179,7 @@ def main(): for fn in ["config.json"]: src = os.path.join(a.indir, fn) if os.path.exists(src): shutil.copy(src, a.outdir) - print(f"convertito {len(shards)} shard -> {a.outdir}") + print(f"converted {len(shards)} shards -> {a.outdir}") return # reale: scarica shard per shard, converte, cancella @@ -211,7 +212,7 @@ def main(): lock = open(os.path.join(a.outdir, ".convert.lock"), "w") try: fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB) except OSError: - print("ERRORE: un altro convertitore sta gia' lavorando su questa outdir. Esco."); return + print("ERROR: another converter is already using this output directory. Exiting."); return # dimensioni note dei file, riempite dopo repo_info: il downloader multi-stream le usa # per calcolare i confini dei segmenti e per sapere quando un file e' completo. @@ -280,13 +281,13 @@ def main(): except Exception as ex: with log_lock: nres[0] += 1 - print(f" [dl] s{t}: {type(ex).__name__} a/at {(s0+done[t])/1e9:.2f} GB: " - f"riprendo/resuming (#{nres[0]})", flush=True) + print(f" [dl] s{t}: {type(ex).__name__} at {(s0+done[t])/1e9:.2f} GB: " + f"resuming (#{nres[0]})", flush=True) _t.sleep(min(15, 1 + nres[0] // NS)) th = [threading.Thread(target=worker, args=(t,), daemon=True) for t in range(NS)] for x in th: x.start() - print(f" [dl {_t.strftime('%H:%M:%S')}] connesso/connected: {NS} stream, " - f"{sum(done)/1e9:.2f} di/of {expected/1e9:.2f} GB", flush=True) + print(f" [dl {_t.strftime('%H:%M:%S')}] connected: {NS} streams, " + f"{sum(done)/1e9:.2f} of {expected/1e9:.2f} GB", flush=True) mark = sum(done); tmark = t0 while any(x.is_alive() for x in th): _t.sleep(5) @@ -309,7 +310,7 @@ def main(): os.replace(part, out) dt = max(_t.time() - t0, 1e-9) print(f" [dl] {fn}: {expected/1e9:.2f} GB in {dt/60:.1f} min " - f"({expected/dt/1e6:.1f} MB/s medi/avg, {NS} stream, {nres[0]} riprese/resumes)", flush=True) + f"({expected/dt/1e6:.1f} MB/s avg, {NS} streams, {nres[0]} resumes)", flush=True) return out def _download_single(url, fn, out, part, expected): @@ -335,9 +336,9 @@ def main(): cl = r.headers.get("Content-Length") if cl: expected = have + int(cl) if have == 0 or nres: # segnale di vita subito / immediate sign of life - print(f" [dl {_t.strftime('%H:%M:%S')}] connesso/connected" + print(f" [dl {_t.strftime('%H:%M:%S')}] connected" f"{f' @ {have/1e9:.2f} GB' if have else ''}" - f"{f' di/of {expected/1e9:.2f} GB' if expected else ''}", flush=True) + f"{f' of {expected/1e9:.2f} GB' if expected else ''}", flush=True) with open(part, "ab" if have else "wb") as f: if not have: f.truncate(0) while True: @@ -357,16 +358,16 @@ def main(): except urllib.error.HTTPError as ex: if ex.code == 416: break # gia' completo / already complete nres += 1 - print(f" [dl] HTTP {ex.code} a/at {have/1e9:.2f} GB: riprendo/resuming (#{nres})", flush=True) + print(f" [dl] HTTP {ex.code} at {have/1e9:.2f} GB: resuming (#{nres})", flush=True) _t.sleep(min(15, 1 + nres)) except Exception as ex: nres += 1 - print(f" [dl] {type(ex).__name__} a/at {have/1e9:.2f} GB: riprendo/resuming (#{nres})", flush=True) + print(f" [dl] {type(ex).__name__} at {have/1e9:.2f} GB: resuming (#{nres})", flush=True) _t.sleep(min(15, 1 + nres)) os.replace(part, out) dt = max(_t.time() - t0, 1e-9); sz = os.path.getsize(out) print(f" [dl] {fn}: {sz/1e9:.2f} GB in {dt/60:.1f} min " - f"({sz/dt/1e6:.1f} MB/s medi/avg, {nres} riprese/resumes)", flush=True) + f"({sz/dt/1e6:.1f} MB/s avg, {nres} resumes)", flush=True) return out from safetensors.numpy import save_file @@ -380,7 +381,7 @@ def main(): break except KeyboardInterrupt: raise except Exception as ex: - w = min(60, 5*(att+1)); print(f"repo_info KO ({type(ex).__name__}): riprovo tra {w}s", flush=True); _t.sleep(w) + w = min(60, 5*(att+1)); print(f"repo_info failed ({type(ex).__name__}); retrying in {w}s", flush=True); _t.sleep(w) shards = sorted(s.rfilename for s in info.siblings if s.rfilename.endswith(".safetensors")) for fn in ["config.json", "tokenizer.json", "tokenizer_config.json", "generation_config.json"]: try: shutil.copy(hf_hub_download(a.repo, fn, local_dir=a.outdir+"/_meta"), a.outdir) @@ -392,19 +393,19 @@ def main(): f"https://huggingface.co/{a.repo}/resolve/main/model.safetensors.index.json", timeout=30).read())["weight_map"] pref = f"model.layers.{a.n_layers}." mtp_shards = sorted(set(v for k, v in idx.items() if k.startswith(pref))) - print(f"[MTP] testa nel layer {a.n_layers}: {len(mtp_shards)} shard da processare: {mtp_shards}") + print(f"[MTP] head at layer {a.n_layers}: {len(mtp_shards)} shards to process: {mtp_shards}") for i, sh in enumerate(mtp_shards): outp = os.path.join(a.outdir, f"out-mtp-{i:05d}.safetensors") - if os.path.exists(outp): print(f"[MTP] {outp} gia' fatto"); continue - print(f"[MTP {i+1}/{len(mtp_shards)}] scarico {sh}...", flush=True) + if os.path.exists(outp): print(f"[MTP] {outp} already done"); continue + print(f"[MTP {i+1}/{len(mtp_shards)}] downloading {sh}...", flush=True) p = download_retry(a.repo, sh, tmp) out = {}; convert_shard(p, out, a.n_layers, a.ebits, a.io_bits, a.xbits, keep_mtp=True) save_file(out, outp) os.remove(p) for blob in glob.glob(os.path.join(tmp, "**", "*"), recursive=True): if os.path.isfile(blob): os.remove(blob) - print(f" -> {os.path.basename(outp)} ({os.path.getsize(outp)/1e9:.2f} GB, {len(out)} tensori)", flush=True) - shutil.rmtree(tmp, ignore_errors=True); print("[MTP] FATTO."); return + print(f" -> {os.path.basename(outp)} ({os.path.getsize(outp)/1e9:.2f} GB, {len(out)} tensors)", flush=True) + shutil.rmtree(tmp, ignore_errors=True); print("[MTP] DONE."); return if a.indexer: import urllib.request idx = json.loads(urllib.request.urlopen( @@ -412,25 +413,25 @@ def main(): idx_shards = sorted(set(v for k, v in idx.items() if "indexer" in k and 0 <= layer_idx(k) < a.n_layers)) tot_gb = len(idx_shards) * 5.4 - print(f"[IDX] pesi indexer su {len(idx_shards)} shard (~{tot_gb:.0f} GB di download totale, resumabile)") + print(f"[IDX] indexer weights across {len(idx_shards)} shards (~{tot_gb:.0f} GB total download, resumable)") for i, sh in enumerate(idx_shards): outp = os.path.join(a.outdir, f"out-idx-{i:05d}.safetensors") if os.path.exists(outp): continue # gia' fatto -> ripartibile - print(f"[IDX {i+1}/{len(idx_shards)}] scarico {sh}...", flush=True) + print(f"[IDX {i+1}/{len(idx_shards)}] downloading {sh}...", flush=True) p = download_retry(a.repo, sh, tmp) out = {}; convert_shard(p, out, a.n_layers, a.ebits, a.io_bits, a.xbits, keep_idx=True) if out: save_file(out, outp) os.remove(p) for blob in glob.glob(os.path.join(tmp, "**", "*"), recursive=True): if os.path.isfile(blob): os.remove(blob) - print(f" -> {os.path.basename(outp)} ({len(out)} tensori)", flush=True) - shutil.rmtree(tmp, ignore_errors=True); print("[IDX] FATTO."); return + print(f" -> {os.path.basename(outp)} ({len(out)} tensors)", flush=True) + shutil.rmtree(tmp, ignore_errors=True); print("[IDX] DONE."); return for i, sh in enumerate(shards): if free_gb(a.outdir) < a.min_free_gb: - print(f"STOP: spazio libero < {a.min_free_gb} GB. Libera spazio e rilancia (riprende)."); break + print(f"STOP: free space is below {a.min_free_gb} GB. Free space and rerun to resume."); break outp = os.path.join(a.outdir, f"out-{i:05d}.safetensors") if os.path.exists(outp): continue # gia' fatto -> ripartibile - print(f"[{i+1}/{len(shards)}] scarico {sh} (libero {free_gb(a.outdir):.0f} GB)...", flush=True) + print(f"[{i+1}/{len(shards)}] downloading {sh} ({free_gb(a.outdir):.0f} GB free)...", flush=True) p = download_retry(a.repo, sh, tmp) out = {}; convert_shard(p, out, a.n_layers, a.ebits, a.io_bits, a.xbits) save_file(out, outp) @@ -439,7 +440,7 @@ def main(): if os.path.isfile(blob): os.remove(blob) print(f" -> {os.path.basename(outp)} ({os.path.getsize(outp)/1e9:.2f} GB)", flush=True) shutil.rmtree(tmp, ignore_errors=True) - print("FATTO." if i == len(shards)-1 else "INTERROTTO (rilancia per riprendere).") + print("DONE." if i == len(shards)-1 else "INTERRUPTED (rerun to resume).") if __name__ == "__main__": main() diff --git a/c/tools/download_glm52.py b/c/tools/download_glm52.py index 544a3f6..aa0c791 100644 --- a/c/tools/download_glm52.py +++ b/c/tools/download_glm52.py @@ -28,15 +28,15 @@ def check(): sts = [s for s in info.siblings if s.rfilename.endswith(".safetensors")] free = shutil.disk_usage(os.path.dirname(DEST) or "/").free print(f"repo: {REPO}") - print(f" file totali: {len(info.siblings)} ({len(sts)} shard safetensors)") - print(f" dimensione totale: {human(tot)}") - print(f" spazio libero in {DEST}: {human(free)}") - print(f" {'OK: ci sta' if free > tot*1.05 else 'ATTENZIONE: spazio insufficiente'}") + print(f" total files: {len(info.siblings)} ({len(sts)} safetensors shards)") + print(f" total size: {human(tot)}") + print(f" free space in {DEST}: {human(free)}") + print(f" {'OK: enough space' if free > tot*1.05 else 'WARNING: not enough space'}") def download(): os.makedirs(DEST, exist_ok=True) free = shutil.disk_usage(DEST).free - print(f"Scarico {REPO} -> {DEST} (libero: {human(free)})") + print(f"Downloading {REPO} -> {DEST} (free: {human(free)})") # resume_download e' implicito; in caso di interruzione, rilancia e riprende. snapshot_download( repo_id=REPO, @@ -44,7 +44,7 @@ def download(): allow_patterns=["*.safetensors", "*.json", "*.txt", "*.model"], max_workers=8, ) - print("FATTO. Pesi in:", DEST) + print("DONE. Weights saved in:", DEST) if __name__ == "__main__": if "--check" in sys.argv: diff --git a/c/tools/eval_glm.py b/c/tools/eval_glm.py index 83778b1..b167253 100644 --- a/c/tools/eval_glm.py +++ b/c/tools/eval_glm.py @@ -43,7 +43,7 @@ def load_docs(task, data_dir, limit, seed): return SMOKE[:limit] if limit else SMOKE path = os.path.join(data_dir, task + ".jsonl") if not os.path.exists(path): - sys.exit(f"manca {path} — generalo con: python3 tools/fetch_benchmarks.py --out {data_dir} --tasks {task}") + sys.exit(f"missing {path} — generate it with: python3 tools/fetch_benchmarks.py --out {data_dir} --tasks {task}") docs = [json.loads(l) for l in open(path) if l.strip()] random.Random(seed).shuffle(docs) return docs[:limit] if limit else docs @@ -84,9 +84,9 @@ def score_accuracy(tasks, meta, perq, lp): print(f"{t:<18} {n:>4} {100*acc/n:>6.1f}% {100*accn/n:>8.1f}%") overall.append(100 * accn / n) for mdl, sc in REFERENCE.get(t, {}).items(): - if sc is not None: print(f"{' rif '+mdl:<18} {'':>4} {'':>7} {sc:>8.1f}%") + if sc is not None: print(f"{' ref '+mdl:<18} {'':>4} {'':>7} {sc:>8.1f}%") if overall: - print(f"\nMEDIA acc_norm: {sum(overall)/len(overall):.1f}% su {len(overall)} task") + print(f"\nMEAN acc_norm: {sum(overall)/len(overall):.1f}% across {len(overall)} tasks") def main(): ap = argparse.ArgumentParser() @@ -99,8 +99,8 @@ def main(): ap.add_argument("--cap", type=int, default=64) ap.add_argument("--bits", default="") ap.add_argument("--seed", type=int, default=1234) - ap.add_argument("--dry", action="store_true", help="costruisci le richieste e fermati (no motore)") - ap.add_argument("--selftest", action="store_true", help="verifica la matematica dello scoring") + ap.add_argument("--dry", action="store_true", help="build requests and stop without running the engine") + ap.add_argument("--selftest", action="store_true", help="verify the scoring calculations") a = ap.parse_args() if a.selftest: # acc/acc_norm con logprob sintetici @@ -113,32 +113,32 @@ def main(): tk = Tokenizer.from_file(os.path.join(a.snap, "tokenizer.json")) tasks = [t.strip() for t in a.tasks.split(",") if t.strip()] docs_by_task = {t: load_docs(t, a.data, a.limit, a.seed) for t in tasks} - for t, d in docs_by_task.items(): print(f"[{t}] {len(d)} domande", file=sys.stderr) + for t, d in docs_by_task.items(): print(f"[{t}] {len(d)} questions", file=sys.stderr) reqs, meta, perq = build_requests(tk, docs_by_task) - print(f"richieste totali: {len(reqs)} (opzioni)", file=sys.stderr) + print(f"total requests: {len(reqs)} (answer options)", file=sys.stderr) if a.dry: - for r in reqs[:3]: print(" esempio req:", r[:80], "...", file=sys.stderr) - print("DRY: meccanica ok (tokenizzazione+richieste). Niente motore.", file=sys.stderr); return + for r in reqs[:3]: print(" example request:", r[:80], "...", file=sys.stderr) + print("DRY: request construction and tokenization passed. Engine was not run.", file=sys.stderr); return req_path = tempfile.mktemp(suffix=".txt") open(req_path, "w").write("\n".join(reqs) + "\n") env = dict(os.environ, SNAP=a.snap, SCORE=req_path) if a.ram: env["RAM_GB"] = str(a.ram) cmd = [a.glm, str(a.cap)] + a.bits.split() - print("eseguo:", " ".join(cmd), file=sys.stderr) + print("running:", " ".join(cmd), file=sys.stderr) t0 = time.time() proc = subprocess.run(cmd, env=env, capture_output=True, text=True) if proc.returncode != 0: - print("ERRORE motore:\n", proc.stderr[-2000:], file=sys.stderr); sys.exit(1) + print("ENGINE ERROR:\n", proc.stderr[-2000:], file=sys.stderr); sys.exit(1) lines = [l for l in proc.stdout.strip().splitlines() if l and l[0] in "-0123456789"] if len(lines) != len(reqs): - print(f"ATTENZIONE: {len(lines)} output vs {len(reqs)} richieste", file=sys.stderr) + print(f"WARNING: {len(lines)} outputs for {len(reqs)} requests", file=sys.stderr) lp = [float(l.split()[0]) for l in lines] - print(f"(motore: {time.time()-t0:.0f}s){proc.stderr.strip().splitlines()[-1] if proc.stderr.strip() else ''}", file=sys.stderr) + print(f"(engine: {time.time()-t0:.0f}s){proc.stderr.strip().splitlines()[-1] if proc.stderr.strip() else ''}", file=sys.stderr) score_accuracy(tasks, meta, perq, lp) - print("\nNB: confronta acc_norm col punteggio PUBBLICATO di GLM-5.2 (model card). Se vicino," - "\n la quantizzazione int4 ha preservato il modello. (riempi REFERENCE in tools/eval_glm.py)") + print("\nNOTE: compare acc_norm with GLM-5.2's PUBLISHED model-card score. A close result" + "\n indicates that int4 quantization preserved quality. (Fill REFERENCE in tools/eval_glm.py.)") os.remove(req_path) if __name__ == "__main__": diff --git a/c/tools/fetch_benchmarks.py b/c/tools/fetch_benchmarks.py index d0069dc..6d2abd2 100644 --- a/c/tools/fetch_benchmarks.py +++ b/c/tools/fetch_benchmarks.py @@ -48,7 +48,7 @@ def main(): from datasets import load_dataset os.makedirs(a.out, exist_ok=True) for t in [x.strip() for x in a.tasks.split(",") if x.strip()]: - if t not in TASKS: print("task ignoto:", t); continue + if t not in TASKS: print("unknown task:", t); continue path, cfg, split, fn = TASKS[t] ds = load_dataset(path, cfg, split=split) idx = list(range(len(ds))); random.Random(a.seed).shuffle(idx) diff --git a/c/tools/gen_unicode.py b/c/tools/gen_unicode.py index 780baa2..20f66e3 100644 --- a/c/tools/gen_unicode.py +++ b/c/tools/gen_unicode.py @@ -41,7 +41,7 @@ def emit(name, rs): print("};") print(f"static const int {name}_n = {len(rs)};\n") -print("/* GENERATO da tools/gen_unicode.py — non modificare a mano. */") +print("/* GENERATED by tools/gen_unicode.py — do not edit by hand. */") print("#ifndef TOK_UNICODE_H\n#define TOK_UNICODE_H\n#include \n") emit("uni_L", L); emit("uni_N", N); emit("uni_S", S) print("""static int uni_in(const uint32_t t[][2], int n, uint32_t cp){ diff --git a/c/tools/make_glm_oracle.py b/c/tools/make_glm_oracle.py index 9eb6780..65a1c19 100644 --- a/c/tools/make_glm_oracle.py +++ b/c/tools/make_glm_oracle.py @@ -53,7 +53,7 @@ with torch.no_grad(): layer.mlp.gate.e_score_correction_bias.copy_( torch.linspace(-0.1, 0.1, cfg.n_routed_experts)) -print("=== tensori dello state_dict (nomi per il loader C) ===") +print("=== state_dict tensors (names used by the C loader) ===") for n, p in model.state_dict().items(): print(f" {n:60s} {tuple(p.shape)}") @@ -76,4 +76,4 @@ print("tf_pred:", tf_pred) model.save_pretrained("glm_tiny", safe_serialization=True) json.dump(cfg.to_dict(), open("glm_tiny/config.json", "w")) json.dump({"prompt_ids": prompt, "full_ids": full, "tf_pred": tf_pred}, open("ref_glm.json", "w")) -print("\nsalvato: glm_tiny/ (pesi+config) e ref_glm.json") +print("\nsaved: glm_tiny/ (weights + config) and ref_glm.json")