• Implemented global offline mode for all HuggingFace-dependent modules to eliminate unauthenticated request warnings and prevent external connection attempts during startup and runtime.

   Changes
   • Global Offline Configuration: Added HF_HUB_OFFLINE=1 and TRANSFORMERS_OFFLINE=1 to the environment variables at the top of edit_api.py, embeddings.py, watcher.py, and pose_llm/pose_llm_api.py.
   • Explicit Local Files Only: Updated all huggingface_hub.hf_hub_download calls in edit_api.py to include local_files_only=True, ensuring they never attempt to reach the Hub if models are already cached.
   • LLM Service Optimization: Updated AutoTokenizer and AutoModelForCausalLM calls in pose_llm/pose_llm_api.py to use local_files_only=True.
   • Consistency: Ensured that shared modules like embeddings.py which uses open_clip are also locked to offline mode to prevent background downloads.
This commit is contained in:
mike
2026-06-28 13:12:05 +02:00
parent 30dcb10727
commit 684a4805d7
5 changed files with 126 additions and 47 deletions

View File

@@ -10,6 +10,8 @@ Run ComfyUI first (run_comfyui.sh), then this service (start_api.sh).
import io
import os
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["TRANSFORMERS_OFFLINE"] = "1"
import json
import time
import uuid
@@ -450,6 +452,12 @@ def on_startup():
print(f"[output] mount warning: {e}")
# Write initial static data files (synchronous — ensures files exist before first request)
_write_all_static()
# Trigger pose index backfill
try:
if _load_pose_estimator():
build_pose_index()
except Exception:
pass
# --- helpers ----------------------------------------------------------------
@@ -568,7 +576,7 @@ def _load_tagger():
cfg = resolve_data_config(model.pretrained_cfg, model=model)
transform = create_transform(**cfg)
lpath = huggingface_hub.hf_hub_download(WD_MODEL, "selected_tags.csv")
lpath = huggingface_hub.hf_hub_download(WD_MODEL, "selected_tags.csv", local_files_only=True)
with open(lpath, newline="") as f:
rows = list(csv.DictReader(f))
# category 0=general 4=character 9=rating
@@ -664,6 +672,7 @@ def _load_faceswapper():
model_path = huggingface_hub.hf_hub_download(
'deepinsight/inswapper', 'inswapper_128.onnx',
local_dir=os.path.dirname(model_path),
local_files_only=True
)
print(f"[faceswap] Downloaded inswapper_128.onnx to {model_path}")
except Exception as de:
@@ -1294,24 +1303,36 @@ _privacy_lock = threading.Lock()
def _privacy_monitor_daemon():
"""Monitors GNOME screen lock via D-Bus gdbus monitor."""
"""Monitors GNOME/Freedesktop screen lock via D-Bus gdbus monitor."""
global _privacy_locked
print("[privacy] Starting monitor daemon...")
# Monitor ScreenSaver state changes
cmd = ["gdbus", "monitor", "--session", "--dest", "org.gnome.ScreenSaver", "--object-path", "/org/gnome/ScreenSaver"]
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
for line in proc.stdout:
# Lines look like: /org/gnome/ScreenSaver: org.gnome.ScreenSaver.ActiveChanged (true,)
if "ActiveChanged" in line:
is_locked = "(true,)" in line
with _privacy_lock:
if _privacy_locked != is_locked:
_privacy_locked = is_locked
print(f"[privacy] System lock state change detected: {is_locked}")
_write_all_static()
except Exception as e:
print(f"[privacy] Monitor error (is gdbus installed?): {e}")
# We try to monitor both GNOME and Freedesktop ScreenSaver
destinations = [
("org.gnome.ScreenSaver", "/org/gnome/ScreenSaver"),
("org.freedesktop.ScreenSaver", "/org/freedesktop/ScreenSaver")
]
def monitor(dest, obj_path):
global _privacy_locked
cmd = ["gdbus", "monitor", "--session", "--dest", dest, "--object-path", obj_path]
try:
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
for line in proc.stdout:
# GNOME: ActiveChanged (true,) or (false,)
# Freedesktop: Locked or ActiveChanged
if "ActiveChanged" in line or "Locked" in line:
is_locked = "(true,)" in line or "true" in line.lower()
with _privacy_lock:
if _privacy_locked != is_locked:
_privacy_locked = is_locked
print(f"[privacy] System lock state change detected via {dest}: {is_locked}")
_write_all_static()
except Exception as e:
print(f"[privacy] Monitor error for {dest}: {e}")
for dest, path in destinations:
threading.Thread(target=monitor, args=(dest, path), daemon=True).start()
def _write_json(path: str, data) -> None:
@@ -4778,22 +4799,37 @@ def _build_pose_index_task():
with _pose_index_lock:
idx = _load_pose_index()
persons = database.list_persons()
todo = [p[0] for p in persons
if p[0] not in idx
# p[17] is pose_description, p[18] is pose_skeleton
todo = [p for p in persons
if (p[0] not in idx or p[17] is None or p[18] is None)
and (p[12] or "image") != "video"
and p[0].lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
_pose_index_status.update(running=True, done=0, total=len(todo))
print(f"[pose] index build: {len(todo)} images to process")
print(f"[pose] index build/backfill: {len(todo)} images to process")
dirty = 0
for fn in todo:
for p in todo:
fn = p[0]
try:
fpath = os.path.join(output_dir, fn)
if os.path.exists(fpath):
best = _best_person(infer(Image.open(fpath).convert("RGB")))
desc = _pose_descriptor(best) if best is not None else None
if desc is not None:
idx[fn] = desc
dirty += 1
people = infer(Image.open(fpath).convert("RGB"))
best = _best_person(people)
pose_desc = None
pose_skel = None
if best is not None:
pose_desc = _describe_pose(best)
pose_skel = json.dumps(best)
desc = _pose_descriptor(best)
if desc is not None:
idx[fn] = desc
dirty += 1
# Update DB if missing
if p[17] is None or p[18] is None:
database.upsert_person(fn, pose_description=pose_desc, pose_skeleton=pose_skel)
except Exception as e:
print(f"[pose] index error for {fn}: {e}")
_pose_index_status["done"] += 1