harpreetsahota's picture
Upload folder using huggingface_hub
087ddd7 verified
Raw
History Blame Contribute Delete
10.4 kB
"""
Launches a diagnostic FiftyOne App for a Hugging Face Docker Space.
The application downloads Hub datasets into ephemeral local storage, imports
their records into MongoDB, and creates browser-local clones on demand.
"""
import asyncio
import faulthandler
import importlib.metadata
import json
import logging
import os
import platform
import shutil
import socket
import sys
import time
from hypercorn.asyncio import serve
from hypercorn.config import Config
from huggingface_hub import snapshot_download
import fiftyone as fo
from gateway import create_gateway
EXTERNAL_APP_ADDRESS = "0.0.0.0"
EXTERNAL_APP_PORT = int(os.environ.get("PORT", "7860"))
INTERNAL_APP_ADDRESS = "127.0.0.1"
INTERNAL_APP_PORT = 5151
MANIFEST_PATH = os.path.join(os.path.dirname(__file__), "datasets.json")
LOG_FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
logging.basicConfig(
level=os.environ.get("LOG_LEVEL", "INFO").upper(),
format=LOG_FORMAT,
stream=sys.stdout,
force=True,
)
logger = logging.getLogger(__name__)
def main():
"""Loads the configured datasets and serves the FiftyOne App."""
faulthandler.enable(file=sys.stderr, all_threads=True)
faulthandler.dump_traceback_later(300, repeat=True, file=sys.stderr)
_log_runtime_diagnostics()
try:
manifest = _load_manifest()
datasets = _load_datasets(manifest)
session = _launch_app(datasets[manifest["default_dataset"]])
except Exception:
logger.exception("Fatal error while starting the FiftyOne App")
raise
finally:
faulthandler.cancel_dump_traceback_later()
logger.info(
"FiftyOne App is ready; starting the browser session gateway"
)
try:
asyncio.run(_serve_gateway(datasets, manifest))
finally:
session.close()
def _load_manifest():
"""Loads and validates the dataset manifest."""
with open(MANIFEST_PATH) as file:
manifest = json.load(file)
entries = manifest.get("datasets")
if not isinstance(entries, list) or not entries:
raise ValueError("datasets.json must contain at least one dataset")
required_fields = {
"name",
"repo_id",
"local_path",
"expected_samples",
}
names = []
for entry in entries:
missing = required_fields - entry.keys()
if missing:
raise ValueError(
f"Dataset manifest entry is missing fields: {sorted(missing)}"
)
if not os.path.isabs(entry["local_path"]):
raise ValueError(
f"Dataset local_path must be absolute: {entry['local_path']}"
)
if not entry["name"] or "/" in entry["name"]:
raise ValueError(
f"Dataset name must be a nonempty path segment: {entry['name']}"
)
if (
not isinstance(entry["expected_samples"], int)
or entry["expected_samples"] < 0
):
raise ValueError(
"Dataset expected_samples must be a nonnegative integer: "
f"{entry['expected_samples']}"
)
names.append(entry["name"])
if len(names) != len(set(names)):
raise ValueError("Dataset names in datasets.json must be unique")
if manifest.get("default_dataset") not in names:
raise ValueError(
"default_dataset must match a dataset name in datasets.json"
)
logger.info(
"Dataset manifest loaded: default=%s datasets=%s",
manifest["default_dataset"],
names,
)
return manifest
def _load_datasets(manifest):
"""Downloads and imports fresh datasets from the Hugging Face Hub."""
logger.info("Connecting to the FiftyOne database")
existing_datasets = fo.list_datasets()
logger.info(
"Database connection succeeded; existing datasets=%s",
existing_datasets,
)
entries = manifest["datasets"]
session_prefixes = tuple(
entry["name"] + "-session-" for entry in entries
)
stale_clones = [
name
for name in existing_datasets
if name.startswith(session_prefixes)
]
for name in stale_clones:
logger.info("Deleting stale browser dataset clone '%s'", name)
fo.delete_dataset(name)
for entry in entries:
dataset_name = entry["name"]
if dataset_name in existing_datasets:
logger.info("Deleting stale base dataset '%s'", dataset_name)
fo.delete_dataset(dataset_name)
datasets = {}
for entry in entries:
dataset_name = entry["name"]
dataset_dir = entry["local_path"]
expected_samples = entry["expected_samples"]
logger.info(
"Downloading Hub dataset: repo=%s directory=%s",
entry["repo_id"],
dataset_dir,
)
download_started_at = time.monotonic()
snapshot_download(
repo_id=entry["repo_id"],
repo_type="dataset",
local_dir=dataset_dir,
)
logger.info(
"Hub dataset downloaded in %.2f seconds: repo=%s directory=%s",
time.monotonic() - download_started_at,
entry["repo_id"],
dataset_dir,
)
logger.info(
"Importing local dataset: name=%s directory=%s",
dataset_name,
dataset_dir,
)
started_at = time.monotonic()
dataset = fo.Dataset.from_dir(
dataset_dir=dataset_dir,
dataset_type=fo.types.FiftyOneDataset,
name=dataset_name,
persistent=True,
)
elapsed = time.monotonic() - started_at
logger.info(
"Dataset imported in %.2f seconds: name=%s samples=%d "
"media_type=%s",
elapsed,
dataset.name,
len(dataset),
dataset.media_type,
)
if len(dataset) != expected_samples:
raise RuntimeError(
f"{dataset_name} import count mismatch: expected "
f"{expected_samples}, found {len(dataset)}"
)
logger.info(
"Dataset validation succeeded: name=%s expected_episodes=%d",
dataset_name,
expected_samples,
)
logger.info(
"Dataset schema: name=%s schema=%s",
dataset_name,
dataset.get_field_schema(),
)
logger.info(
"Dataset disk usage: name=%s stats=%s",
dataset_name,
dataset.stats(include_media=True),
)
first_sample = dataset.first()
logger.info(
"First sample diagnostics: dataset=%s id=%s filepath=%s "
"file_exists=%s",
dataset_name,
first_sample.id,
first_sample.filepath,
os.path.isfile(first_sample.filepath),
)
datasets[dataset_name] = dataset
return datasets
def _launch_app(dataset):
"""Launches the internal App behind the browser session gateway."""
logger.info(
"Launching internal FiftyOne App: address=%s port=%d remote=True",
INTERNAL_APP_ADDRESS,
INTERNAL_APP_PORT,
)
started_at = time.monotonic()
session = fo.launch_app(
dataset,
address=INTERNAL_APP_ADDRESS,
port=INTERNAL_APP_PORT,
remote=True,
)
elapsed = time.monotonic() - started_at
logger.info(
"FiftyOne session launched in %.2f seconds: url=%s server=%s:%s",
elapsed,
session.url,
session.server_address,
session.server_port,
)
return session
async def _serve_gateway(datasets, manifest):
"""Serves the external session-aware gateway."""
config = Config()
config.bind = [f"{EXTERNAL_APP_ADDRESS}:{EXTERNAL_APP_PORT}"]
config.accesslog = "-"
config.errorlog = "-"
config.graceful_timeout = 10
config.keep_alive_timeout = 30
logger.info(
"Serving session gateway: address=%s port=%d",
EXTERNAL_APP_ADDRESS,
EXTERNAL_APP_PORT,
)
shared_media_roots = [
entry["local_path"] for entry in manifest["datasets"]
]
await serve(
create_gateway(
datasets,
manifest["default_dataset"],
shared_media_roots,
),
config,
)
def _log_runtime_diagnostics():
"""Logs non-secret runtime details useful for diagnosing Space failures."""
disk = shutil.disk_usage("/")
package_names = (
"fiftyone",
"fiftyone-db",
"pymongo",
"starlette",
"hypercorn",
"httpx",
)
package_versions = {}
for package_name in package_names:
try:
package_versions[package_name] = importlib.metadata.version(
package_name
)
except importlib.metadata.PackageNotFoundError:
package_versions[package_name] = "not-installed"
logger.info("Starting FiftyOne Hugging Face Space")
logger.info(
"Runtime: python=%s executable=%s platform=%s architecture=%s",
sys.version.replace("\n", " "),
sys.executable,
platform.platform(),
platform.machine(),
)
logger.info(
"Process: pid=%d uid=%d gid=%d hostname=%s cwd=%s",
os.getpid(),
os.getuid(),
os.getgid(),
socket.gethostname(),
os.getcwd(),
)
logger.info(
"Environment: HOME=%s PORT=%s LOG_LEVEL=%s",
os.environ.get("HOME"),
os.environ.get("PORT"),
os.environ.get("LOG_LEVEL"),
)
logger.info("Package versions=%s", package_versions)
logger.info(
"Disk '/': total=%.2fGiB used=%.2fGiB free=%.2fGiB",
disk.total / 1024**3,
disk.used / 1024**3,
disk.free / 1024**3,
)
logger.info(
"FiftyOne config: database_dir=%s database_uri=%s "
"dataset_zoo_dir=%s model_zoo_dir=%s default_app_port=%s "
"default_app_address=%s",
getattr(fo.config, "database_dir", None),
getattr(fo.config, "database_uri", None),
getattr(fo.config, "dataset_zoo_dir", None),
getattr(fo.config, "model_zoo_dir", None),
getattr(fo.config, "default_app_port", None),
getattr(fo.config, "default_app_address", None),
)
if __name__ == "__main__":
main()