Add Hugging Face support and enhance model resolution logic
- Introduced support for Hugging Face API integration, allowing fallback model resolution when Civitai fails. - Updated configuration to include `HF_TOKEN` and `HF_TOKEN_PATH` for authentication. - Enhanced model capture logic to differentiate between Civitai and Hugging Face sources. - Improved error handling for model downloads, providing clearer messages for authentication issues. - Updated documentation to reflect new environment variables and usage instructions for Hugging Face integration. - Added tests to validate the new fallback mechanism and ensure robust model resolution.
This commit is contained in:
+75
-25
@@ -234,16 +234,10 @@ def seed_swarmui_api_keys(cfg: Config, host: str, log: Log) -> None:
|
||||
Swarm stores them in Users.ldb GenericData — needed for Model Downloader in the UI.
|
||||
Call after SwarmUI HTTP is up (after wait_backend / verify).
|
||||
"""
|
||||
import os
|
||||
|
||||
keys: dict[str, str] = {}
|
||||
if cfg.civitai_api_token:
|
||||
keys["civitai_api"] = cfg.civitai_api_token
|
||||
hf = (
|
||||
os.environ.get("HF_TOKEN")
|
||||
or os.environ.get("HUGGING_FACE_HUB_TOKEN")
|
||||
or ""
|
||||
).strip()
|
||||
hf = (cfg.hf_token or "").strip()
|
||||
if hf:
|
||||
keys["huggingface_api"] = hf
|
||||
if not keys:
|
||||
@@ -273,21 +267,52 @@ def seed_swarmui_api_keys(cfg: Config, host: str, log: Log) -> None:
|
||||
|
||||
|
||||
def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
from gpu_rent.huggingface import is_huggingface_url
|
||||
|
||||
entries = parse_models(cfg.models_manifest)
|
||||
if not cfg.civitai_api_token:
|
||||
log("Civitai-seed пропущен: нет CIVITAI_API_TOKEN — дефолт SwarmUI")
|
||||
return
|
||||
if not entries:
|
||||
log("Civitai-seed пропущен: манифест пуст — дефолт SwarmUI")
|
||||
log("model-seed пропущен: манифест пуст — дефолт SwarmUI")
|
||||
return
|
||||
jobs = []
|
||||
|
||||
jobs: list[dict] = []
|
||||
for entry in entries:
|
||||
url = (entry.url or "").strip()
|
||||
if url and is_huggingface_url(url):
|
||||
name = url.rstrip("/").rsplit("/", 1)[-1].split("?", 1)[0] or "model.safetensors"
|
||||
folder = MODEL_DIRS.get(entry.kind, entry.kind)
|
||||
dest = f"{DATA}/Models/{folder}/{name}"
|
||||
stem = Path(name).stem
|
||||
swarm = {
|
||||
"name": stem,
|
||||
"title": stem,
|
||||
"description": f"Hugging Face: {url}",
|
||||
"trigger_phrase": "",
|
||||
"author": "",
|
||||
"tags": ["huggingface"],
|
||||
}
|
||||
jobs.append(
|
||||
{
|
||||
"dest": dest,
|
||||
"url": url,
|
||||
"sha256": "",
|
||||
"auth": "hf",
|
||||
"sidecars": {
|
||||
f"{stem}.swarm.json": json.dumps(swarm, ensure_ascii=False, indent=2),
|
||||
},
|
||||
}
|
||||
)
|
||||
continue
|
||||
|
||||
if not cfg.civitai_api_token:
|
||||
continue
|
||||
vid = entry.version_id or (extract_version_id(entry.url) if entry.url else None)
|
||||
if not vid:
|
||||
log(f"пропуск {entry.kind}: нет version_id")
|
||||
log(f"пропуск {entry.kind}: нет version_id / HF url")
|
||||
continue
|
||||
try:
|
||||
api_host, version = fetch_model_version(cfg.civitai_api_token, cfg.civitai_api_host, vid)
|
||||
api_host, version = fetch_model_version(
|
||||
cfg.civitai_api_token, cfg.civitai_api_host, vid
|
||||
)
|
||||
except CloudError as exc:
|
||||
log(str(exc))
|
||||
continue
|
||||
@@ -309,7 +334,11 @@ def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
"title": version.get("name") or stem,
|
||||
"description": (version.get("description") or "")[:2000],
|
||||
"trigger_phrase": phrase,
|
||||
"author": ((version.get("model") or {}) if isinstance(version.get("model"), dict) else {}).get("name"),
|
||||
"author": (
|
||||
((version.get("model") or {}) if isinstance(version.get("model"), dict) else {}).get(
|
||||
"name"
|
||||
)
|
||||
),
|
||||
"tags": version.get("tags") or [],
|
||||
}
|
||||
jobs.append(
|
||||
@@ -317,22 +346,43 @@ def seed_civitai(cfg: Config, host: str, log: Log) -> None:
|
||||
"dest": dest,
|
||||
"url": _download_url(api_host, vid, info),
|
||||
"sha256": sha,
|
||||
"auth": "civitai",
|
||||
"sidecars": {
|
||||
f"{stem}.civitai.json": civitai_json,
|
||||
f"{stem}.swarm.json": json.dumps(swarm, ensure_ascii=False, indent=2),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
if not jobs:
|
||||
log("Civitai-seed: ни одной скачиваемой строки")
|
||||
if not cfg.civitai_api_token:
|
||||
log("Civitai-seed пропущен: нет CIVITAI_API_TOKEN — дефолт SwarmUI")
|
||||
else:
|
||||
log("Civitai-seed: ни одной скачиваемой строки")
|
||||
return
|
||||
|
||||
hf_n = sum(1 for j in jobs if j.get("auth") == "hf")
|
||||
civ_n = len(jobs) - hf_n
|
||||
if hf_n and not cfg.hf_token:
|
||||
log(
|
||||
"⚠ в манифесте есть Hugging Face URL, но нет HF_TOKEN — "
|
||||
"gated/abliterated файлы дадут 401. Токен: huggingface.co/settings/tokens"
|
||||
)
|
||||
if civ_n and not cfg.civitai_api_token:
|
||||
log("⚠ Civitai-строки пропущены: нет CIVITAI_API_TOKEN")
|
||||
|
||||
if not any(e.kind == "checkpoint" for e in entries):
|
||||
log("в манифесте нет checkpoint — генерация может не стартовать")
|
||||
put_text(cfg, host, "/tmp/gpu-rent-civitai-jobs.json", json.dumps(jobs, indent=2))
|
||||
put_text(cfg, host, "/tmp/gpu-rent-civitai.token", cfg.civitai_api_token + "\n", mode=0o600)
|
||||
if cfg.civitai_api_token:
|
||||
put_text(
|
||||
cfg, host, "/tmp/gpu-rent-civitai.token", cfg.civitai_api_token + "\n", mode=0o600
|
||||
)
|
||||
if cfg.hf_token:
|
||||
put_text(cfg, host, "/tmp/gpu-rent-hf.token", cfg.hf_token + "\n", mode=0o600)
|
||||
log(
|
||||
f"Civitai: {len(jobs)} в манифесте — на VM качаю отсутствующие "
|
||||
f"(уже есть + sha → skip; прогресс [N/{len(jobs)}])"
|
||||
f"model-seed: {len(jobs)} файл(ов) "
|
||||
f"(civitai={civ_n}, huggingface={hf_n}) — прогресс [N/{len(jobs)}]"
|
||||
)
|
||||
run_python(
|
||||
cfg,
|
||||
@@ -419,7 +469,6 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
gguf_filename_from_url,
|
||||
parse_llamacpp_models,
|
||||
)
|
||||
import os
|
||||
|
||||
entries = parse_llamacpp_models(cfg.llamacpp_models_manifest)
|
||||
defaults = [e for e in entries if e.default]
|
||||
@@ -439,13 +488,14 @@ def provision_llm(cfg: Config, host: str, log: Log) -> None:
|
||||
put_text(
|
||||
cfg, host, "/tmp/gpu-rent-llamacpp-models.json", json.dumps(jobs, indent=2)
|
||||
)
|
||||
hf = (
|
||||
os.environ.get("HF_TOKEN")
|
||||
or os.environ.get("HUGGING_FACE_HUB_TOKEN")
|
||||
or ""
|
||||
).strip()
|
||||
hf = (cfg.hf_token or "").strip()
|
||||
if hf:
|
||||
put_text(cfg, host, "/tmp/gpu-rent-hf.token", hf + "\n", mode=0o600)
|
||||
else:
|
||||
log(
|
||||
"⚠ HF_TOKEN не задан — gated GGUF (abliterated и др.) часто дают 401. "
|
||||
"Добавь HF_TOKEN=hf_… в .env → https://huggingface.co/settings/tokens"
|
||||
)
|
||||
log(f"llama.cpp: скачиваю {len(jobs)} GGUF из манифеста")
|
||||
run_python(
|
||||
cfg,
|
||||
|
||||
Reference in New Issue
Block a user