dataset-utils / app.py
giangndm's picture
fix: cookie-free Sign in with Hugging Face (works from the huggingface.co embed)
6b85415 verified
Raw History Blame Contribute Delete
5.62 kB
"""
Dataset Utils
=============
A Gradio Space bundling several utilities for datasets on the Hugging Face Hub. This file
only does the routing: a shared sign-in section, one tab per feature (`features/<name>.py`,
see `features/__init__.py`) and the πŸ“‹ Jobs tab. Every long-running task is a job on the
server-side queue in `core/jobs.py` β€” the backbone of the app.
Features:
- 🧲 Text Column Extract β€” extract a text column into a split of another dataset.
- πŸ—œοΈ Audio Compress β€” re-encode audio columns to FLAC / Opus / MP3, shard by shard.
"""
from __future__ import annotations
import gradio as gr
from core import hf_login, jobs_tab
from core.common import (
CSS,
GRADIO_MAJOR,
AppContext,
do_logout,
do_token_login,
login_badge,
on_page_load,
poll_oauth_login,
start_oauth_login,
)
from features import FEATURES
#: Gradio 6 moved `theme`/`css` from Blocks to launch().
BLOCKS_KWARGS: dict = {"title": "Dataset Utils"}
LAUNCH_KWARGS: dict = {"show_error": True}
if GRADIO_MAJOR >= 6:
LAUNCH_KWARGS.update(theme=gr.themes.Soft(), css=CSS)
else:
BLOCKS_KWARGS.update(theme=gr.themes.Soft(), css=CSS)
# ---------------------------------------------------------------------------
# ZeroGPU
# ---------------------------------------------------------------------------
# This Space runs on ZeroGPU hardware (the free tier for personal accounts).
# ZeroGPU requires at least one `@spaces.GPU` function at startup, otherwise it fails
# with "No @spaces.GPU function detected during startup".
# The app is CPU-only, so this is just a "heartbeat" function never called from the UI;
# per the ZeroGPU docs, the decorator is a no-op outside ZeroGPU.
try: # pragma: no cover
import spaces # type: ignore
@spaces.GPU
def _zerogpu_heartbeat() -> str:
"""Only satisfies ZeroGPU's startup requirement β€” never called from the UI."""
return "ok"
except Exception: # noqa: BLE001 - no ZeroGPU here: skip
pass
# ---------------------------------------------------------------------------
# UI
# ---------------------------------------------------------------------------
with gr.Blocks(**BLOCKS_KWARGS) as demo:
gr.Markdown(
"""
# 🧰 Dataset Utils
Utilities for datasets on the Hugging Face Hub β€” one tab per feature.
"""
)
ctx = AppContext(session_token=gr.State(None), submitted_job=gr.State(None))
session_token = ctx.session_token
# ---------------------- 1. Sign in (shared) ----------------------
with gr.Accordion("πŸ” 1. Sign in to Hugging Face", open=True):
with gr.Row():
with gr.Column(scale=1):
login_button = gr.Button(
"Sign in with Hugging Face",
variant="huggingface",
icon="https://huggingface.co/front/assets/huggingface_logo-noborder.svg",
)
gr.Markdown(
"<sub>Opens Hugging Face in a new tab; once you approve, this page signs in "
"by itself (no cookies needed, so it also works when the app is embedded on "
"huggingface.co). OAuth requests the <code>read-repos</code>, "
"<code>write-repos</code> and <code>manage-repos</code> scopes to create repos "
"and push data.</sub>"
)
oauth_nonce = gr.Textbox(visible="hidden")
oauth_poll = gr.Timer(2.0, active=False)
with gr.Column(scale=1):
token_box = gr.Textbox(
label="Or paste an Access Token",
placeholder="hf_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
type="password",
info="Create one at https://huggingface.co/settings/tokens (Write permission).",
)
with gr.Row():
login_btn = gr.Button("Sign in with token", variant="primary")
logout_btn = gr.Button("Sign out")
login_status = gr.Markdown(login_badge())
# ---------------------- Features + Jobs ----------------------
with gr.Tabs() as tabs:
ctx.tabs = tabs
for feature in FEATURES:
with gr.Tab(feature.TITLE, id=feature.__name__.rsplit(".", 1)[-1]):
feature.build(ctx)
with gr.Tab(jobs_tab.TITLE, id=jobs_tab.TAB_ID):
jobs_tab.build(ctx)
# ---------------------- Sign-in events ----------------------
# Note: `gr.OAuthProfile` / `gr.OAuthToken` parameters are injected by Gradio from
# the type hint and must NOT be listed in `inputs`.
demo.load(
on_page_load,
inputs=[session_token],
outputs=[session_token, login_status],
).then(None, js=hf_login.CLEAN_URL_JS)
login_button.click(None, js=hf_login.login_js(), outputs=[oauth_nonce]).then(
start_oauth_login, inputs=[oauth_nonce], outputs=[login_status, oauth_poll]
)
oauth_poll.tick(
poll_oauth_login,
inputs=[oauth_nonce, session_token],
outputs=[session_token, login_status, oauth_nonce, oauth_poll],
concurrency_limit=None,
show_progress="hidden",
)
login_btn.click(
do_token_login,
inputs=[token_box, session_token],
outputs=[session_token, login_status],
).then(lambda: "", outputs=[token_box])
logout_btn.click(do_logout, inputs=[session_token], outputs=[session_token, login_status])
if __name__ == "__main__":
demo.queue(max_size=16, default_concurrency_limit=1).launch(**LAUNCH_KWARGS)