#!/usr/bin/env python3 """60_publicar_v060.py — Publicação da v0.6.0 no HF (UM ÚNICO upload_folder organizado — o HF bloqueia envios parciais em sequência e em curto espaço de tempo; uma única operação atômica evita o bloqueio). Fases: 1. inventário da conta + APAGAMENTO de estados de treino remanescentes (qualquer repo — nenhum deve existir); 2. varredura local do VALOR do token (nenhum arquivo pode contê-lo); 3. UM ÚNICO upload_folder (sem estados — checkpoints regeneráveis); 4. verificação pós-upload (0 estados, módulos-chave presentes); 5. varredura REMOTA do valor do token (baixa e inspeciona todos os arquivos). Token: EXCLUSIVAMENTE via variável de ambiente HF_TOKEN (nunca em arquivo). """ import os, sys, tempfile ARQUIVOS_EXCLUIR_DIRS = {"target", ".cargo", "__pycache__", ".git"} ARQUIVOS_EXCLUIR_NOME = {"shard_treino.jsonl", "shard_validacao.jsonl"} # checkpoints/estados NUNCA publicados (regeneráveis via scripts 56/40) SUFIXOS_ESTADO = (".pt", ".pth", ".ckpt", ".safetensors", ".bin", ".npz", ".msgpack") NOMES_ESTADO = {"gerador_conceitos.json", "tokenizador_conceitos.json"} def coletar(pasta): arquivos = {} for raiz, dirs, fs in os.walk(pasta): dirs[:] = [d for d in dirs if d not in ARQUIVOS_EXCLUIR_DIRS] for f in fs: if f in ARQUIVOS_EXCLUIR_NOME or f in NOMES_ESTADO: continue if f.endswith(SUFIXOS_ESTADO): continue if f.endswith(".so") or f == ".gitignore": continue caminho = os.path.join(raiz, f) rel = os.path.relpath(caminho, pasta) arquivos[rel] = caminho return arquivos def varre_token_em_arquivos(token, pares): """Varre o VALOR do token em arquivos locais (menções TEXTUAIS ao CONCEITO 'HF_TOKEN' são legítimas e não contêm o valor).""" tb = token.encode() achados = [] for rel, p in pares.items(): try: with open(p, "rb") as f: if tb in f.read(): achados.append(rel) except OSError: pass return achados def main(): from huggingface_hub import HfApi token = os.environ.get("HF_TOKEN") if not token: print("ERRO: HF_TOKEN ausente"); sys.exit(1) api = HfApi(token=token) repo = "PowerMachine/khtst-rust" # ---------------- 1) inventário da conta + apaga estados ---------------- print("=== 1) inventário da conta HF (procura estados de treino) ===") repos = list(api.list_models(author="PowerMachine")) print(f"repos: {len(repos)}") total_removidos = 0 for r in repos: try: files = api.list_repo_files(repo_id=r.id, repo_type="model") except Exception as e: print(f" {r.id}: erro ao listar ({e})"); continue estados = [f for f in files if f.endswith(SUFIXOS_ESTADO) or f.split("/")[-1] in NOMES_ESTADO or (".meta.json" in f and "checkpoint" in f)] if estados: print(f" {r.id}: APAGANDO {len(estados)} estados: {estados[:5]}{'...' if len(estados) > 5 else ''}") for f in estados: api.delete_file(path_in_repo=f, repo_id=r.id, repo_type="model") total_removidos += len(estados) else: print(f" {r.id}: {len(files)} arquivos, 0 estados") print(f"estados removidos no total: {total_removidos}") # ---------------- 2) varredura local do token ---------------- pasta = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) arquivos = coletar(pasta) print(f"\n=== 2) varredura local: {len(arquivos)} arquivos ===") achados = varre_token_em_arquivos(token, arquivos) if achados: print(f"ERRO: valor do token encontrado em: {achados}"); sys.exit(1) print("varredura de token local: limpa") # também varre o home (configs/caches) import subprocess home = os.path.expanduser("~") out = subprocess.run(["grep", "-rl", token, home, "--include=*", "--exclude-dir=.venv", "--exclude-dir=skills", "--exclude-dir=my-project"], capture_output=True, text=True) if out.stdout.strip(): print(f"ERRO: token em arquivos do home: {out.stdout.strip()}"); sys.exit(1) print("varredura do home: limpa") # ---------------- 3) UM ÚNICO upload_folder ---------------- print(f"\n=== 3) publicando {len(arquivos)} arquivos em {repo} (upload ÚNICO) ===") total_mb = sum(os.path.getsize(p) for p in arquivos.values()) / 1e6 print(f"tamanho total: {total_mb:.1f} MB") api.upload_folder( repo_id=repo, repo_type="model", folder_path=pasta, ignore_patterns=[ "target/*", ".cargo/*", "__pycache__/*", "*.so", ".gitignore", "dados/shard_*.jsonl", "resultados/gerador_conceitos.json", "resultados/tokenizador_conceitos.json", "*.pt", "*.ckpt", "*.safetensors", "*.bin", "*.npz", ], commit_message=("v0.6.0: verificador NCM calibrado (sem oráculo, V-1..V-4) + " "treino conjunto Pensamento↔backbone (fase C, PCGrad, acoplador duplex AC) + " "MoEs redirecionáveis c/ atenção própria (M-R1..M-R5, JEPA+SOM) + " "autoajuste por telemetria (A-1..A-5, UCB1) — 46/46 gates, provas r-23..r-27"), ) print("upload concluído") # ---------------- 4) verificação pós-upload ---------------- print("\n=== 4) verificação pós-upload ===") files = api.list_repo_files(repo_id=repo, repo_type="model") print(f"repo agora tem {len(files)} arquivos") estados = [f for f in files if f.endswith(SUFIXOS_ESTADO) or f.split("/")[-1] in NOMES_ESTADO] print(f"estados de treino no repo: {len(estados)} {estados if estados else '(limpo)'}") for chave in ("src/verificador.rs", "src/moe_redirecionavel.rs", "src/acoplador.rs", "docs/memorias/r-27-resultados-v060.md", "scripts/59_suite_v060.py", "scripts/60_publicar_v060.py", "resultados/suite_v060_final.json"): print(f" {'OK ' if chave in files else 'FALTA'} {chave}") # ---------------- 5) varredura REMOTA do token ---------------- print("\n=== 5) varredura remota do valor do token ===") tb = token.encode() suspeitos = [] with tempfile.TemporaryDirectory() as tmp: for f in files: try: destino = os.path.join(tmp, f.replace("/", "_")) api.hf_hub_download(repo_id=repo, filename=f, repo_type="model", local_dir=tmp) # hf_hub_download preserva o nome relativo cand = os.path.join(tmp, f) alvo = cand if os.path.exists(cand) else destino if os.path.exists(alvo): with open(alvo, "rb") as fh: if tb in fh.read(): suspeitos.append(f) except Exception: pass if suspeitos: print(f"ERRO: valor do token em arquivos REMOTOS: {suspeitos}"); sys.exit(1) print(f"varridos {len(files)} arquivos remotos: limpos") print("PUBLICACAO_OK") if __name__ == "__main__": main()