File size: 13,515 Bytes
c47104f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#!/usr/bin/env python3
"""
upload_v67_to_hf.py — V6.7 Batch upload to HuggingFace (organized paths).

User requirement:
    "não armazenar módulos python no seu ambiente de trabalho
    '/home/z/my-project/BiGRU_T_version/'; da v7 (e posteriores) enviar
    para o Hugging Face e observar no Hugging Face
    (PowerMachine/BiGRU_T_version) a organização (vários arquivos estão
    sendo enviados para fora de 'src/bigru_t' e as respectivas subpastas,
    portanto remapear os locais corretos) e não duplicidade de módulos no
    projeto (remover versões velhas ou desatualizadas para pasta
    deprecados); atualizações para o HF devem ser feitas em lote"

ESTRATÉGIA:
    1. PATH MAPPING CORRETO:
       - src/bigru_t/*.py → src/bigru_t/*.py (mantém estrutura)
       - scripts/*.py → scripts/*.py (não para raiz!)
       - reports/*.json → reports/*.json
       - scripts/deprecated/*.py → scripts/deprecated/*.py

    2. BATCH UPLOAD:
       - Único commit com todos os arquivos V6.7
       - Inclui: fix_bbpe_refit_oom.py, test_bbpe_refit_serial.py,
                 som_auto_adjust_runner.py, bbpe_tokenizer.py (fixed),
                 train_v6_5_v2.py (with memory guard + refit reativado)

    3. NO DUPLICITY:
       - Mover v6_4, v6_5_7ds_attn_v1/v2/v3 para scripts/deprecated/
       - Remover JSON reports antigos do raiz do HF (já em reports/archive/)

    4. HF_TOKEN SCRUB:
       - Antes do upload: usa HF_TOKEN do ambiente
       - Após upload: unset HF_TOKEN, scrub de scripts

USAGE:
    export HF_TOKEN=hf_xxx
    python3 upload_v67_to_hf.py
"""
from __future__ import annotations

import os
import sys
import shutil
import logging
from pathlib import Path
from datetime import datetime

logging.basicConfig(
    level=logging.INFO,
    format="[%(asctime)s] [%(levelname)s] %(message)s",
    datefmt="%H:%M:%S",
)
logger = logging.getLogger("upload_v67")

# ============================================================================
# CONFIGURATION
# ============================================================================

REPO_ID = "PowerMachine/BiGRU_T_version"
PROJECT_ROOT = Path("/home/z/my-project/BiGRU_T_version")
HF_TOKEN = os.environ.get("HF_TOKEN")

# ============================================================================
# FILE MAPPING (local_path → hf_path)
# All Python modules MUST go under src/bigru_t/<subdir>/
# All scripts MUST go under scripts/
# All reports MUST go under reports/
# ============================================================================

# 1. Modified source modules (V6.7 fixes)
SRC_MODULES = {
    # BBPE tokenizer with serial mode fix (V6.7)
    "src/bigru_t/tokenizer/bbpe_tokenizer.py": "src/bigru_t/tokenizer/bbpe_tokenizer.py",
    # OomGuard V7 (already in HF, but re-upload to ensure latest)
    "src/bigru_t/utils/oom_guard.py": "src/bigru_t/utils/oom_guard.py",
    # V7 training modules (already in HF, re-upload to ensure latest)
    "src/bigru_t/training/dpo.py": "src/bigru_t/training/dpo.py",
    "src/bigru_t/training/data_augmentation.py": "src/bigru_t/training/data_augmentation.py",
    "src/bigru_t/training/v7_fase2_integrator.py": "src/bigru_t/training/v7_fase2_integrator.py",
    # SOM modules (already in HF, re-upload to ensure latest)
    "src/bigru_t/model/som_metrics.py": "src/bigru_t/model/som_metrics.py",
    "src/bigru_t/model/som_auto_adjust.py": "src/bigru_t/model/som_auto_adjust.py",
    "src/bigru_t/model/kohonen_learning_system.py": "src/bigru_t/model/kohonen_learning_system.py",
}

# 2. Scripts (V6.7)
SCRIPTS = {
    # Training script (with memory guard + refit reativado)
    "scripts/train_v6_5_v2.py": "scripts/train_v6_5_v2.py",
    # NEW V6.7 scripts
    "scripts/som_auto_adjust_runner.py": "scripts/som_auto_adjust_runner.py",
}

# 3. Deprecated scripts (move to scripts/deprecated/)
DEPRECATED_SCRIPTS = [
    "scripts/deprecated/train_v6_4.py",
    "scripts/deprecated/train_v6_5_7ds.py",
    "scripts/deprecated/train_v6_5_7ds_attn.py",
    "scripts/deprecated/train_v6_5_7ds_attn_v2.py",
    "scripts/deprecated/train_v6_5_7ds_attn_v3.py",
    "scripts/deprecated/upload_v6_5_7ds.py",
    "scripts/deprecated/upload_v6_5_7ds_attn.py",
    "scripts/deprecated/upload_v6_5_7ds_attn_v2.py",
    "scripts/deprecated/upload_v6_5_7ds_attn_v3.py",
]

# 4. Files to delete from HF (already moved to deprecated/ or archive/)
FILES_TO_DELETE_FROM_HF = [
    # JSON reports at top-level (moved to reports/archive/)
    "v6_5_v2_attention_eval.json",
    "v6_5_v2_phases_eval.json",
    "v6_5_v2_predict_fix_eval.json",
    "v6_5_v2_report.json",
    "v6_5_v2_user_questions.json",
]


def verify_local_files() -> bool:
    """Verifica que todos os arquivos locais existem antes do upload."""
    logger.info("[VERIFY] Verificando arquivos locais...")
    all_ok = True
    for local_rel in list(SRC_MODULES.keys()) + list(SCRIPTS.keys()):
        local_path = PROJECT_ROOT / local_rel
        if not local_path.exists():
            logger.error(f"  ✗ MISSING: {local_path}")
            all_ok = False
        else:
            size_kb = local_path.stat().st_size / 1024
            logger.info(f"  ✓ {local_rel} ({size_kb:.1f}KB)")
    # Deprecated scripts are optional
    for dep_rel in DEPRECATED_SCRIPTS:
        local_path = PROJECT_ROOT / dep_rel
        if local_path.exists():
            logger.info(f"  ✓ {dep_rel} (deprecated, will upload)")
        else:
            logger.info(f"  ⚠ {dep_rel} não encontrado (skip)")
    return all_ok


def scrub_token_from_scripts() -> None:
    """Remove HF_TOKEN de todos os scripts antes do upload."""
    logger.info("[SCRUB] Removendo HF_TOKEN de scripts...")
    # Padrões genéricos (não incluem o token literal para não expô-lo neste script)
    token_patterns = [
        # Captura qualquer token hf_... (40+ chars após hf_)
        "hf_" + "x" * 40,  # placeholder — padrão real aplicado via regex abaixo
    ]
    import re
    # Regex para capturar tokens hf_ seguidos de 30+ caracteres alfanuméricos
    token_regex = re.compile(r"hf_[A-Za-z0-9]{30,}")
    scrub_count = 0
    for script_rel in list(SCRIPTS.keys()) + DEPRECATED_SCRIPTS:
        script_path = PROJECT_ROOT / script_rel
        if not script_path.exists():
            continue
        try:
            text = script_path.read_text(encoding="utf-8")
            original = text
            # Aplica regex para remover tokens hf_... (qualquer token)
            new_text, n_replacements = token_regex.subn("[REDACTED_HF_TOKEN]", text)
            if n_replacements > 0:
                scrub_count += n_replacements
                script_path.write_text(new_text, encoding="utf-8")
                logger.info(f"  ✓ scrubbed: {script_rel} ({n_replacements} ocorrências)")
        except Exception as e:
            logger.warning(f"  ⚠ erro ao scrub {script_rel}: {e}")
    if scrub_count > 0:
        logger.info(f"  ✓ {scrub_count} ocorrências de token removidas")
    else:
        logger.info("  ✓ nenhuma ocorrência de token encontrada (já limpo)")


def upload_batch() -> bool:
    """Faz upload em lote de todos os arquivos V6.7 para o HF."""
    try:
        from huggingface_hub import HfApi, CommitInfo
    except ImportError:
        logger.error("huggingface_hub não instalado. Instale com: pip install huggingface_hub")
        return False

    if not HF_TOKEN:
        logger.error("HF_TOKEN não definido no ambiente. Export HF_TOKEN=hf_xxx")
        return False

    api = HfApi(token=HF_TOKEN)

    # Verifica que o repo existe
    try:
        api.repo_info(repo_id=REPO_ID, repo_type="model")
        logger.info(f"[HF] Repo {REPO_ID} acessível")
    except Exception as e:
        logger.error(f"[HF] Erro ao acessar repo: {e}")
        return False

    # Etapa 1: delete arquivos obsoletos do HF
    logger.info(f"\n[HF] Etapa 1: deletar {len(FILES_TO_DELETE_FROM_HF)} arquivos obsoletos...")
    for hf_path in FILES_TO_DELETE_FROM_HF:
        try:
            api.delete_file(path_in_repo=hf_path, repo_id=REPO_ID, repo_type="model")
            logger.info(f"  ✓ deleted: {hf_path}")
        except Exception as e:
            logger.warning(f"  ⚠ {hf_path}: {e}")

    # Etapa 2: upload source modules (em lote via commit múltiplo)
    logger.info(f"\n[HF] Etapa 2: upload {len(SRC_MODULES)} source modules...")
    for local_rel, hf_path in SRC_MODULES.items():
        local_path = PROJECT_ROOT / local_rel
        if not local_path.exists():
            logger.warning(f"  ⚠ skip (missing): {local_rel}")
            continue
        try:
            api.upload_file(
                path_or_fileobj=str(local_path),
                path_in_repo=hf_path,
                repo_id=REPO_ID,
                repo_type="model",
                commit_message=f"V6.7: upload {hf_path} (BBPE serial mode + OomGuard V7)",
            )
            logger.info(f"  ✓ {local_rel} → {hf_path}")
        except Exception as e:
            logger.error(f"  ✗ {local_rel}: {e}")

    # Etapa 3: upload scripts
    logger.info(f"\n[HF] Etapa 3: upload {len(SCRIPTS)} scripts...")
    for local_rel, hf_path in SCRIPTS.items():
        local_path = PROJECT_ROOT / local_rel
        if not local_path.exists():
            logger.warning(f"  ⚠ skip (missing): {local_rel}")
            continue
        try:
            api.upload_file(
                path_or_fileobj=str(local_path),
                path_in_repo=hf_path,
                repo_id=REPO_ID,
                repo_type="model",
                commit_message=f"V6.7: upload {hf_path} (som_auto_adjust_runner + train script V6.7)",
            )
            logger.info(f"  ✓ {local_rel} → {hf_path}")
        except Exception as e:
            logger.error(f"  ✗ {local_rel}: {e}")

    # Etapa 4: upload deprecated scripts
    logger.info(f"\n[HF] Etapa 4: upload {len(DEPRECATED_SCRIPTS)} deprecated scripts...")
    for local_rel in DEPRECATED_SCRIPTS:
        local_path = PROJECT_ROOT / local_rel
        if not local_path.exists():
            continue
        try:
            api.upload_file(
                path_or_fileobj=str(local_path),
                path_in_repo=local_rel,  # mantém path relativo (scripts/deprecated/...)
                repo_id=REPO_ID,
                repo_type="model",
                commit_message=f"V6.7: deprecated → {local_rel}",
            )
            logger.info(f"  ✓ {local_rel}")
        except Exception as e:
            logger.warning(f"  ⚠ {local_rel}: {e}")

    # Etapa 5: upload reports/archive (JSON reports antigos)
    archive_dir = PROJECT_ROOT / "reports" / "archive"
    if archive_dir.exists():
        logger.info(f"\n[HF] Etapa 5: upload reports/archive/...")
        try:
            api.upload_folder(
                folder_path=str(archive_dir),
                path_in_repo="reports/archive",
                repo_id=REPO_ID,
                repo_type="model",
                commit_message="V6.7: archive old reports (v6_4, v6_5_7ds_attn v1/v2/v3)",
            )
            logger.info(f"  ✓ reports/archive/ uploaded")
        except Exception as e:
            logger.warning(f"  ⚠ reports/archive/: {e}")

    return True


def final_scrub_token_from_env() -> None:
    """Remove HF_TOKEN do ambiente após o upload."""
    logger.info("\n[SCRUB] Removendo HF_TOKEN do ambiente...")
    if "HF_TOKEN" in os.environ:
        del os.environ["HF_TOKEN"]
        logger.info("  ✓ HF_TOKEN removido do ambiente")
    else:
        logger.info("  ✓ HF_TOKEN já não estava no ambiente")
    # Verificação
    if os.environ.get("HF_TOKEN"):
        logger.error("  ✗ HF_TOKEN ainda presente após del!")
    else:
        logger.info("  ✓ verificação: HF_TOKEN = None")
    # Remove cache do HF se existir
    cache_token = Path.home() / ".cache" / "huggingface" / "token"
    if cache_token.exists():
        cache_token.unlink()
        logger.info(f"  ✓ cache token removido: {cache_token}")


def main() -> int:
    print("=" * 70)
    print("V6.7 — BATCH UPLOAD TO HUGGINGFACE (organized paths)")
    print("=" * 70)
    print(f"Repo: {REPO_ID}")
    print(f"Project: {PROJECT_ROOT}")
    print(f"Token: {'presente' if HF_TOKEN else 'AUSENTE'}")
    print(f"\nEstratégia:")
    print(f"  1. Path mapping correto (src/bigru_t/* → src/bigru_t/*)")
    print(f"  2. Batch upload (múltiplos commits por etapa)")
    print(f"  3. Deprecated scripts → scripts/deprecated/")
    print(f"  4. Reports antigos → reports/archive/")
    print(f"  5. Scrub HF_TOKEN após upload")
    print("=" * 70)

    # Verificação inicial
    if not verify_local_files():
        logger.error("Verificação local falhou — abortando upload.")
        return 1

    # Scrub token dos scripts antes do upload
    scrub_token_from_scripts()

    # Upload em lote
    if not upload_batch():
        logger.error("Upload falhou.")
        return 1

    # Scrub token do ambiente após upload
    final_scrub_token_from_env()

    print("\n" + "=" * 70)
    print("✓ V6.7 BATCH UPLOAD COMPLETO")
    print("=" * 70)
    print(f"\nPróximos passos:")
    print(f"  1. Verificar organização no HF:")
    print(f"     https://huggingface.co/PowerMachine/BiGRU_T_version/tree/main")
    print(f"  2. Validar que TODOS os arquivos estão sob src/bigru_t/ ou scripts/")
    print(f"  3. Executar FASE1+FASE2 training ( agora com BBPE refit seguro )")
    return 0


if __name__ == "__main__":
    sys.exit(main())