• 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

@@ -21,6 +21,8 @@ Env:
import asyncio
import json
import os
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["TRANSFORMERS_OFFLINE"] = "1"
import subprocess
import threading
import time
@@ -69,7 +71,7 @@ async def lifespan(app: FastAPI):
# Boost GPU to high performance mode (avoids power-saving clock throttle)
subprocess.run(["/opt/rocm/bin/rocm-smi", "--setperflevel", "high"], capture_output=True)
print(f"Loading model {MODEL_ID} (gpu≤{MAX_GPU_MEM} cpu≤{MAX_CPU_MEM})...", flush=True)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, local_files_only=True)
if not tokenizer.chat_template:
print("No chat_template found — applying Mistral [INST] fallback.", flush=True)
tokenizer.chat_template = _MISTRAL_TEMPLATE
@@ -78,6 +80,7 @@ async def lifespan(app: FastAPI):
torch_dtype=torch.float16,
device_map="auto",
max_memory={0: MAX_GPU_MEM, "cpu": MAX_CPU_MEM},
local_files_only=True,
)
model.eval()
state["tokenizer"] = tokenizer

View File

@@ -2299,6 +2299,25 @@
// --- HYDRATION_START ---
const PRELOADED_IMAGES = [
"_turntable/up_c8be3aa7/views/view_018_270deg.png",
"_turntable/up_c8be3aa7/views/view_017_255deg.png",
"_turntable/up_c8be3aa7/views/view_016_240deg.png",
"_turntable/up_c8be3aa7/views/view_015_225deg.png",
"_turntable/up_c8be3aa7/views/view_014_210deg.png",
"_turntable/up_c8be3aa7/views/view_013_195deg.png",
"_turntable/up_c8be3aa7/views/view_012_180deg.png",
"_turntable/up_c8be3aa7/views/view_011_165deg.png",
"_turntable/up_c8be3aa7/views/view_010_150deg.png",
"_turntable/up_c8be3aa7/views/view_009_135deg.png",
"_turntable/up_c8be3aa7/views/view_008_120deg.png",
"_turntable/up_c8be3aa7/views/view_007_105deg.png",
"_turntable/up_c8be3aa7/views/view_006_090deg.png",
"_turntable/up_c8be3aa7/views/view_005_075deg.png",
"_turntable/up_c8be3aa7/views/view_004_060deg.png",
"_turntable/up_c8be3aa7/views/view_003_045deg.png",
"_turntable/up_c8be3aa7/views/view_002_030deg.png",
"_turntable/up_c8be3aa7/views/view_001_015deg.png",
"_turntable/up_c8be3aa7/views/view_000_000deg.png",
"up_b8c61991_face.png",
"_turntable/bb_01.png/views/view_008_120deg.png",
"_turntable/bb_01.png/views/view_007_105deg.png",
@@ -2789,8 +2808,6 @@
"20260627_203054_Screenshot_From_2026-06-27_20-04-11.png",
"20260627_203044_image.png",
"20260627_203032_image.png",
"_turntable/up_c8be3aa7/views/view_002_030deg.png",
"_turntable/up_c8be3aa7/views/view_003_045deg.png",
"20260627_201807_12_20260627_201343_image.png",
"20260627_201753_Screenshot_From_2026-06-27_12-32-39.png",
"20260627_201751_11_20260627_201343_image.png",
@@ -2807,18 +2824,12 @@
"20260627_201418_1_20260627_201343_image.png",
"20260627_201400_0_20260627_201343_image.png",
"20260627_201343_image.png",
"_turntable/up_c8be3aa7/views/view_007_105deg.png",
"_turntable/up_c8be3aa7/views/view_006_090deg.png",
"_turntable/up_c8be3aa7/views/view_005_075deg.png",
"_turntable/up_c8be3aa7/views/view_004_060deg.png",
"_turntable/up_c8be3aa7/views/view_001_015deg.png",
"20260627_200711_11_20260627_200330_image.png",
"20260627_200506_image.png",
"20260627_200422_2_20260627_200330_image.png",
"20260627_200414_image.png",
"20260627_200405_1_20260627_200330_image.png",
"20260627_200330_image.png",
"_turntable/up_c8be3aa7/views/view_000_000deg.png",
"20260627_200201_12_20260627_195835_image.png",
"20260627_200144_11_20260627_195835_image.png",
"20260627_200127_10_20260627_195835_image.png",
@@ -7497,18 +7508,35 @@
_lastSelectedGid = gid;
localStorage.setItem('lastSelectedGid', gid);
_activeFilmstripTab = 'group';
['Group', 'Hidden', 'Source', 'Archived'].forEach(t => {
// Default to 'active' tab, but fallback to 'group' if empty
const activeNames = data.names.filter(name => !fileHidden[name] && !fileIsSource[name] && !fileArchived[name]);
if (activeNames.length > 0) {
_activeFilmstripTab = 'active';
lbNames = activeNames;
lbUrls = activeNames.map(n => IMAGE_FOLDER + n);
} else {
_activeFilmstripTab = 'group';
lbUrls = data.urls;
lbNames = data.names;
}
['Active', 'Group', 'Hidden', 'Source', 'Archived'].forEach(t => {
const el = document.getElementById(`fsTab${t}`);
if (el) el.classList.toggle('active', t.toLowerCase() === 'group');
if (el) el.classList.toggle('active', t.toLowerCase() === _activeFilmstripTab);
});
_multiSelectModeActive = false;
_selectedFilenames.clear();
updateMultiSelectUI();
lbUrls = data.urls;
lbNames = data.names;
lbIdx = startIdx !== undefined ? startIdx : (cycleIdx.get(gid) || 0);
// If we are in 'active' tab, ensure lbIdx is valid for lbNames
if (_activeFilmstripTab === 'active' && startIdx !== undefined) {
const targetName = data.names[startIdx];
const activeIdx = lbNames.indexOf(targetName);
if (activeIdx !== -1) lbIdx = activeIdx;
else lbIdx = 0; // fallback if the image we clicked is not 'active'
}
updateStudio();
document.getElementById('studio').classList.add('open');
// Hide main page scrollbar — nothing in studio scrolls the page
@@ -9808,14 +9836,22 @@
const r = await fetch(`${API}/output/_data/system_status.json?t=${Date.now()}`, { signal: AbortSignal.timeout(2000) });
if (!r.ok) return;
const data = await r.json();
if (data.locked && !_lastSystemLockState) {
_lastSystemLockState = true;
if (data.locked) {
if (!privacyMode) togglePrivacyMode();
showPrivacyOverlay();
console.log("[privacy] System lock detected, auto-hiding content");
showToast('Privacy lock active (System Locked)', 'info');
} else if (!data.locked) {
_lastSystemLockState = false;
if (!_lastSystemLockState) {
_lastSystemLockState = true;
console.log("[privacy] System lock detected, auto-hiding content");
showToast('Privacy lock active (System Locked)', 'info');
}
} else {
if (_lastSystemLockState) {
_lastSystemLockState = false;
// If it was locked by system, and now system is unlocked,
// we automatically unlock if privacyMode is still active.
if (privacyMode) togglePrivacyMode();
console.log("[privacy] System unlock detected, restoring content");
}
}
} catch (e) {}
}

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

View File

@@ -1,7 +1,9 @@
import torch
import os
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["TRANSFORMERS_OFFLINE"] = "1"
import open_clip
from PIL import Image
import os
import threading
_model = None

View File

@@ -1,4 +1,6 @@
import os
os.environ["HF_HUB_OFFLINE"] = "1"
os.environ["TRANSFORMERS_OFFLINE"] = "1"
import time
import json
import shutil