setup models

This commit is contained in:
Marcos Lohmann
2026-10-07 13:43:09 -03:00
parent aeee6c7de1
commit 84ea9a5307
75 changed files with 5093 additions and 58 deletions

175
env/acquire.py vendored Normal file
View File

@@ -0,0 +1,175 @@
"""Aquisição de artefatos dos candidatos (tarefas 13–16).
Baixa pesos, tokenizadores e licenças nas revisões fixadas, empacota o código-fonte
dos runtimes em commits fixados, calcula SHA-256, envia tudo ao bucket `dmb-artefatos`
e grava o manifesto em env/artifacts-manifest.json.
Uso: PYTHONPATH=infra/oci .venv/bin/python env/acquire.py [--skip-upload]
"""
import argparse
import hashlib
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path
import oci
from huggingface_hub import HfApi, snapshot_download
from oci.object_storage import UploadManager
from session import load_config
ROOT = Path(__file__).resolve().parents[1]
LOCAL = ROOT / "models"
MANIFEST = ROOT / "env" / "artifacts-manifest.json"
BUCKET = "dmb-artefatos"
LICENSE_ALLOWLIST = {"apache-2.0", "mit"}
HF_MODELS = [
{"candidate": "laya", "repo": "convaiinnovations/laya-multilingual",
"revision": "1720e3e3357cfe1e281542e223f8273b0890ca34", "allow": None},
{"candidate": "gliner", "repo": "fastino/GLiNER2.5-multi-Decide",
"revision": "a35a0cd3b7a0f00f2effc576f454cd48fa98aa5f", "allow": None, "ignore": ["*.png"]},
{"candidate": "semif", "repo": "Qwen/Qwen3.5-4B",
"revision": "851bf6e806efd8d0a36b00ddf55e13ccb7b8cd0a", "allow": None},
{"candidate": "rizzo_flow", "repo": "rizzoaiacademy/rizzo-flow",
"revision": "55633c8cbd2b826bd3eefdeb05310450996649df",
"allow": ["spark-x2.5-4b-rizzo-flow-lora-q8_0.gguf", "LICENSE", "README.md", "training/history.json"],
"expected_sha256": {"spark-x2.5-4b-rizzo-flow-lora-q8_0.gguf":
"dbec3c89d33984772324e65a8ed56b48e958e301b856cb870a7c1b385d01691a"}},
]
GIT_SOURCES = [
{"candidate": "semif", "repo": "https://github.com/TheoLeeCJ/SemIf-OpenJev",
"commit": "23cf1f39fc9534fe81437200959b6dfc7106e45a", "license": "mit"},
{"candidate": "rizzo_flow", "repo": "https://github.com/Rizzo-AI-Academy/rizzo-flow",
"commit": "b9ba007ee4d2928bbab5b1d8bfe9009c3696b6de", "license": "apache-2.0"},
]
# Runtime llama.cpp do Rizzo Flow (release b11081): arquivos oficiais baixados por
# `rizzo download --only runtime --runtime {cpu,cuda}`, que confere o SHA-256 fixado pelo projeto.
RUNTIME_ARCHIVES = [
"llama-b11081-bin-ubuntu-x64.tar.gz",
"llama-b11081-bin-ubuntu-cuda-13.4-x64.tar.gz",
"cudart-llama-b11081-bin-ubuntu-cuda-13.4-x64.tar.gz",
]
def sha256(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(1 << 22), b""):
h.update(chunk)
return h.hexdigest()
def fetch_hf(spec: dict) -> list[dict]:
info = HfApi().model_info(spec["repo"], revision=spec["revision"])
lic = (info.card_data or {}).get("license") if info.card_data else None
if (lic or "").lower() not in LICENSE_ALLOWLIST:
sys.exit(f"licença fora da allowlist: {spec['repo']} -> {lic}")
target = LOCAL / spec["candidate"] / spec["repo"].replace("/", "__") / spec["revision"]
snapshot_download(spec["repo"], revision=spec["revision"], local_dir=target,
allow_patterns=spec.get("allow"), ignore_patterns=spec.get("ignore"))
entries = []
for f in sorted(p for p in target.rglob("*") if p.is_file() and ".cache" not in p.parts):
rel = f.relative_to(target).as_posix()
digest = sha256(f)
expected = spec.get("expected_sha256", {}).get(rel)
if expected and expected != digest:
sys.exit(f"SHA-256 divergente em {spec['repo']}/{rel}")
entries.append({
"candidate": spec["candidate"], "kind": "hf_model", "origin": f"hf://{spec['repo']}",
"revision": spec["revision"], "license": lic, "path": rel, "sha256": digest,
"bytes": f.stat().st_size, "local": str(f),
"object": f"models/{spec['candidate']}/{spec['repo'].replace('/', '__')}/{spec['revision']}/{rel}",
})
return entries
def fetch_git(spec: dict) -> list[dict]:
name = spec["repo"].rstrip("/").split("/")[-1]
work = LOCAL / "src" / name
if not (work / ".git").exists():
subprocess.run(["git", "clone", "-q", spec["repo"], str(work)], check=True)
subprocess.run(["git", "-C", str(work), "fetch", "-q", "origin", spec["commit"]], check=False)
subprocess.run(["git", "-C", str(work), "checkout", "-q", spec["commit"]], check=True)
tarball = LOCAL / "src" / f"{name}-{spec['commit'][:12]}.tar.gz"
with open(tarball, "wb") as out:
subprocess.run(["git", "-C", str(work), "archive", "--format=tar.gz",
f"--prefix={name}/", spec["commit"]], stdout=out, check=True)
return [{
"candidate": spec["candidate"], "kind": "source", "origin": spec["repo"],
"revision": spec["commit"], "license": spec["license"], "path": tarball.name,
"sha256": sha256(tarball), "bytes": tarball.stat().st_size, "local": str(tarball),
"object": f"src/{spec['candidate']}/{tarball.name}",
}]
def fetch_runtimes() -> list[dict]:
downloads = LOCAL / "runtimes" / "downloads"
missing = [a for a in RUNTIME_ARCHIVES if not (downloads / a).is_file()]
if missing:
sys.exit(f"runtimes ausentes {missing}: rode `rizzo download --only runtime --runtime cpu|cuda` em models/")
return [{
"candidate": "rizzo_flow", "kind": "runtime", "origin": "https://github.com/ggml-org/llama.cpp/releases/tag/b11081",
"revision": "b11081", "license": "mit", "path": name, "sha256": sha256(downloads / name),
"bytes": (downloads / name).stat().st_size, "local": str(downloads / name),
"object": f"runtimes/rizzo_flow/{name}",
} for name in RUNTIME_ARCHIVES]
def upload(entries: list[dict]) -> None:
cfg = load_config()
client = oci.object_storage.ObjectStorageClient(cfg)
ns = client.get_namespace().data
manager = UploadManager(client, allow_parallel_uploads=True, parallel_process_count=8)
for e in entries:
try:
head = client.head_object(ns, BUCKET, e["object"])
if head.headers.get("opc-meta-sha256") == e["sha256"]:
print("já no bucket:", e["object"])
continue
except oci.exceptions.ServiceError as err:
if err.status != 404:
raise
if sha256(Path(e["local"])) != e["sha256"]:
sys.exit(f"{e['local']} mudou depois do cálculo do hash; abortando o envio")
print(f"enviando {e['object']} ({e['bytes'] / 1e9:.2f} GB)", flush=True)
manager.upload_file(ns, BUCKET, e["object"], e["local"],
metadata={"sha256": e["sha256"], "revision": e["revision"]})
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--skip-upload", action="store_true")
args = ap.parse_args()
entries = []
for spec in HF_MODELS:
print("baixando", spec["repo"], flush=True)
entries += fetch_hf(spec)
for spec in GIT_SOURCES:
print("empacotando", spec["repo"], flush=True)
entries += fetch_git(spec)
entries += fetch_runtimes()
if not args.skip_upload:
upload(entries)
now = datetime.now(timezone.utc).isoformat(timespec="seconds")
manifest = {
"schema": "dmb-artifacts-v1",
"acquired_at": now,
"bucket": BUCKET,
"license_allowlist": sorted(LICENSE_ALLOWLIST),
"artifacts": [{k: v for k, v in e.items() if k != "local"} for e in entries],
}
MANIFEST.write_text(json.dumps(manifest, indent=2, ensure_ascii=False) + "\n")
total = sum(e["bytes"] for e in entries)
print(f"{len(entries)} artefatos, {total / 1e9:.2f} GB; manifesto em {MANIFEST.relative_to(ROOT)}")
if __name__ == "__main__":
main()