Spaces:
Running
Running
| """Descarga un modelo Ollama, exporta su GGUF, lo sube a HF y lo elimina localmente.""" | |
| from __future__ import annotations | |
| import argparse | |
| import os | |
| import re | |
| import shutil | |
| import subprocess | |
| from pathlib import Path | |
| from huggingface_hub import HfApi | |
| from huggingface_hub.errors import RepositoryNotFoundError | |
| DEFAULT_MODEL = "qwen3:4b-instruct" | |
| DEFAULT_REPO = "AlbertiTechnology/qwen3-4b-instruct-gguf" | |
| MAX_MODEL_BYTES = 3 * 1024**3 | |
| def find_ollama() -> str: | |
| executable = shutil.which("ollama") | |
| if executable: | |
| return executable | |
| candidates = [ | |
| Path(os.getenv("LOCALAPPDATA", "")) / "Programs" / "Ollama" / "ollama.exe", | |
| Path(os.getenv("LOCALAPPDATA", "")) / "Ollama" / "ollama.exe", | |
| Path(os.getenv("ProgramFiles", "")) / "Ollama" / "ollama.exe", | |
| ] | |
| for candidate in candidates: | |
| if candidate.is_file(): | |
| return str(candidate) | |
| raise RuntimeError( | |
| "Ollama no esta instalado. Instale Ollama, cierre y abra la terminal, " | |
| "y vuelva a ejecutar este script." | |
| ) | |
| def run_ollama(*args: str, capture: bool = False) -> str: | |
| executable = find_ollama() | |
| result = subprocess.run( | |
| [executable, *args], | |
| check=True, | |
| text=True, | |
| capture_output=capture, | |
| encoding="utf-8", | |
| errors="replace", | |
| ) | |
| return result.stdout if capture else "" | |
| def model_blob_from_modelfile(modelfile: str) -> Path: | |
| match = re.search(r"^FROM\s+(.+?)\s*$", modelfile, flags=re.MULTILINE) | |
| if not match: | |
| raise RuntimeError("No se encontro la capa GGUF en 'ollama show --modelfile'.") | |
| raw_path = match.group(1).strip().strip('"') | |
| blob_path = Path(raw_path) | |
| if not blob_path.is_file(): | |
| raise FileNotFoundError(f"Ollama informo un blob inexistente: {blob_path}") | |
| return blob_path | |
| def exported_modelfile(original: str) -> str: | |
| return re.sub( | |
| r"^FROM\s+.+?$", | |
| "FROM ./model.gguf", | |
| original, | |
| count=1, | |
| flags=re.MULTILINE, | |
| ) | |
| def upload_model(model: str, repo_id: str, token: str) -> None: | |
| print(f"Descargando {model} con Ollama...", flush=True) | |
| run_ollama("pull", model) | |
| modelfile = run_ollama("show", "--modelfile", model, capture=True) | |
| blob_path = model_blob_from_modelfile(modelfile) | |
| blob_size = blob_path.stat().st_size | |
| if blob_size > MAX_MODEL_BYTES: | |
| raise RuntimeError( | |
| f"El GGUF ocupa {blob_size / 1024**3:.2f} GiB y supera el limite de 3 GiB." | |
| ) | |
| readme = "\n".join( | |
| [ | |
| "---", | |
| "library_name: llama.cpp", | |
| "tags:", | |
| "- gguf", | |
| "- ollama", | |
| "- qwen3", | |
| "---", | |
| "", | |
| f"# {model}", | |
| "", | |
| "Modelo GGUF exportado desde Ollama para inferencia local.", | |
| ] | |
| ) | |
| api = HfApi(token=token) | |
| try: | |
| api.repo_info(repo_id=repo_id, repo_type="model") | |
| print(f"Repositorio existente encontrado: {repo_id}", flush=True) | |
| except RepositoryNotFoundError: | |
| api.create_repo(repo_id=repo_id, repo_type="model") | |
| print( | |
| f"Subiendo directamente el blob de {blob_size / 1024**3:.2f} GiB; " | |
| "no se creara una copia local...", | |
| flush=True, | |
| ) | |
| api.upload_file( | |
| path_or_fileobj=blob_path, | |
| path_in_repo="model.gguf", | |
| repo_id=repo_id, | |
| repo_type="model", | |
| commit_message=f"Upload {model} GGUF from Ollama", | |
| ) | |
| api.upload_file( | |
| path_or_fileobj=exported_modelfile(modelfile).encode("utf-8"), | |
| path_in_repo="Modelfile", | |
| repo_id=repo_id, | |
| repo_type="model", | |
| ) | |
| api.upload_file( | |
| path_or_fileobj=readme.encode("utf-8"), | |
| path_in_repo="README.md", | |
| repo_id=repo_id, | |
| repo_type="model", | |
| ) | |
| print(f"Subida completada: https://huggingface.co/{repo_id}", flush=True) | |
| print(f"Eliminando {model} del almacenamiento local de Ollama...", flush=True) | |
| run_ollama("rm", model) | |
| print("Modelo local y archivos temporales eliminados.", flush=True) | |
| def parse_args() -> argparse.Namespace: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--model", default=DEFAULT_MODEL) | |
| parser.add_argument("--repo", default=DEFAULT_REPO) | |
| return parser.parse_args() | |
| def main() -> None: | |
| args = parse_args() | |
| token = os.getenv("HF_TOKEN") or os.getenv("HUGGINGFACEHUB_API_TOKEN") | |
| if not token: | |
| raise RuntimeError("Defina HF_TOKEN con permiso de escritura antes de ejecutar.") | |
| upload_model(args.model, args.repo, token) | |
| if __name__ == "__main__": | |
| main() | |