"""Datology Labs look for the Space, with a light theme (default) and a dark theme. Colors were sampled from the Datology Labs brand film. The light theme follows the film's opening scenes (off-white paper, soft blue glows, navy type); the dark theme follows its night scenes (deep navy, cobalt and teal glows). Both share the cobalt-to-cyan hero, the orange accent and the film grain. A light-bulb button beside the hero switches between them and remembers the choice. Everything visual lives here, so app.py stays the same as the plain version apart from a few imports. """ from __future__ import annotations import inspect import gradio as gr # --------------------------------------------------------------------------- # Palette (sampled from the brand film) # --------------------------------------------------------------------------- # Shared brand colors DEEP_COBALT = "#052d78" COBALT = "#1574eb" ELECTRIC = "#2f8ff0" # primary blue; "kept" in charts CYAN = "#39b0df" ICE = "#a4d9e8" ORANGE = "#e8692a" # the single warm accent; "removed" in charts ORANGE_HI = "#f2894c" EMBER = "#e5b06d" # Dark theme (the night scenes) NIGHT = "#040b18" # deepest background NAVY = "#0a1630" # panels / chart surface NAVY_2 = "#0c2447" # raised surfaces, inputs TEXT = "#f2f6fc" TEXT_MUTED = "#9fb3cf" LINE = "rgba(140, 180, 235, 0.16)" LINE_STRONG = "rgba(140, 180, 235, 0.32)" # Light theme (the opening scenes) PAPER = "#f6f9fd" # page WHITE = "#ffffff" # panels / chart surface PAPER_2 = "#eef3fa" # raised surfaces, inputs INK_NAVY = "#0b1b3a" # body text (navy captions in the film) INK_MUTED = "#4b5e7c" COBALT_TEXT = "#1257c4" # blue that reads as text on white ORANGE_TEXT = "#c9531a" # orange that reads as text on white LINE_L = "rgba(21, 60, 120, 0.12)" LINE_STRONG_L = "rgba(21, 60, 120, 0.24)" FONT = "Hanken Grotesk" FONT_MONO = "JetBrains Mono" # Chart roles. The same two colors pass the palette checks on navy and on white # (CVD ΔE 27, contrast ≥ 3:1), so bars keep their colors in both themes. # Axis text, gridlines and the cutoff line switch with the theme via CSS (see PLOT CSS below). KEPT = ELECTRIC REMOVED = ORANGE INK = TEXT_MUTED GRID = "rgba(159, 179, 207, 0.12)" CUTOFF_LINE = "rgba(242, 246, 252, 0.7)" # --------------------------------------------------------------------------- # Gradio theme: light values for the default, dark values under .dark # --------------------------------------------------------------------------- _SHARED = dict( body_text_weight="400", border_color_accent=ELECTRIC, color_accent=ELECTRIC, shadow_drop="none", shadow_spread="0px", block_border_width="1px", block_radius="16px", container_radius="16px", block_label_background_fill="transparent", block_label_border_width="0px", block_label_text_weight="500", block_title_background_fill="transparent", block_title_border_width="0px", block_title_text_weight="500", block_title_padding="0", panel_background_fill="transparent", input_border_color_focus=ELECTRIC, input_border_width="1px", checkbox_background_color_selected=ELECTRIC, checkbox_border_color_selected=ELECTRIC, checkbox_border_color_focus=ELECTRIC, checkbox_label_border_width="1px", checkbox_label_shadow="none", checkbox_label_text_weight="400", slider_color=ORANGE, loader_color=ORANGE, stat_background_fill=f"linear-gradient(90deg, {COBALT}, {CYAN})", button_primary_background_fill=ORANGE, button_primary_background_fill_hover=ORANGE_HI, button_primary_border_color=ORANGE, button_primary_border_color_hover=ORANGE_HI, button_primary_text_color="#ffffff", button_primary_text_color_hover="#ffffff", button_secondary_border_color_hover=ELECTRIC, button_secondary_shadow="none", ) _DARK = dict( body_background_fill=NIGHT, body_text_color=TEXT, body_text_color_subdued=TEXT_MUTED, background_fill_primary=NAVY, background_fill_secondary=NAVY_2, border_color_primary=LINE, border_color_accent_subdued=LINE_STRONG, color_accent_soft="rgba(47, 143, 240, 0.18)", link_text_color=CYAN, link_text_color_hover=ICE, link_text_color_visited=CYAN, link_text_color_active=ICE, code_background_fill=NIGHT, shadow_drop_lg="0 20px 60px rgba(0, 0, 0, 0.35)", block_background_fill="rgba(10, 22, 48, 0.72)", block_border_color=LINE, block_label_text_color=TEXT_MUTED, block_title_text_color=TEXT, input_background_fill=NAVY_2, input_background_fill_focus=NAVY_2, input_background_fill_hover=NAVY_2, input_border_color=LINE, input_border_color_hover=LINE_STRONG, checkbox_background_color=NIGHT, checkbox_background_color_hover=NIGHT, checkbox_background_color_focus=NIGHT, checkbox_border_color=LINE_STRONG, checkbox_label_background_fill="rgba(12, 36, 71, 0.6)", checkbox_label_background_fill_hover="rgba(21, 116, 235, 0.14)", checkbox_label_background_fill_selected="rgba(21, 116, 235, 0.22)", checkbox_label_border_color=LINE, checkbox_label_border_color_hover=LINE_STRONG, checkbox_label_border_color_selected="rgba(47, 143, 240, 0.65)", checkbox_label_text_color=TEXT, checkbox_label_text_color_selected=TEXT, table_border_color=LINE, table_even_background_fill=NAVY, table_odd_background_fill=NAVY_2, table_row_focus="rgba(21, 116, 235, 0.18)", table_text_color=TEXT, button_primary_shadow="0 0 24px rgba(232, 105, 42, 0.35)", button_primary_shadow_hover="0 0 32px rgba(232, 105, 42, 0.5)", button_secondary_background_fill="rgba(12, 36, 71, 0.6)", button_secondary_background_fill_hover="rgba(21, 116, 235, 0.18)", button_secondary_border_color=LINE_STRONG, button_secondary_text_color=TEXT, button_secondary_text_color_hover=TEXT, ) _LIGHT = dict( body_background_fill=PAPER, body_text_color=INK_NAVY, body_text_color_subdued=INK_MUTED, background_fill_primary=WHITE, background_fill_secondary=PAPER_2, border_color_primary=LINE_L, border_color_accent_subdued=LINE_STRONG_L, color_accent_soft="rgba(21, 116, 235, 0.10)", link_text_color=COBALT_TEXT, link_text_color_hover=COBALT, link_text_color_visited=COBALT_TEXT, link_text_color_active=COBALT, code_background_fill=PAPER, shadow_drop_lg="0 18px 50px rgba(21, 60, 120, 0.10)", block_background_fill="rgba(255, 255, 255, 0.82)", block_border_color=LINE_L, block_label_text_color=INK_MUTED, block_title_text_color=INK_NAVY, input_background_fill=WHITE, input_background_fill_focus=WHITE, input_background_fill_hover=WHITE, input_border_color=LINE_STRONG_L, input_border_color_hover="rgba(21, 60, 120, 0.36)", checkbox_background_color=WHITE, checkbox_background_color_hover=WHITE, checkbox_background_color_focus=WHITE, checkbox_border_color="rgba(21, 60, 120, 0.36)", checkbox_label_background_fill=PAPER_2, checkbox_label_background_fill_hover="rgba(21, 116, 235, 0.08)", checkbox_label_background_fill_selected="rgba(21, 116, 235, 0.10)", checkbox_label_border_color=LINE_L, checkbox_label_border_color_hover=LINE_STRONG_L, checkbox_label_border_color_selected="rgba(21, 116, 235, 0.45)", checkbox_label_text_color=INK_NAVY, checkbox_label_text_color_selected=INK_NAVY, table_border_color=LINE_L, table_even_background_fill=WHITE, table_odd_background_fill=PAPER, table_row_focus="rgba(21, 116, 235, 0.08)", table_text_color=INK_NAVY, button_primary_shadow="0 6px 18px rgba(232, 105, 42, 0.25)", button_primary_shadow_hover="0 8px 24px rgba(232, 105, 42, 0.35)", button_secondary_background_fill=WHITE, button_secondary_background_fill_hover="rgba(21, 116, 235, 0.06)", button_secondary_border_color=LINE_STRONG_L, button_secondary_text_color=INK_NAVY, button_secondary_text_color_hover=INK_NAVY, ) def build_theme() -> gr.themes.Base: theme = gr.themes.Base( primary_hue=gr.themes.colors.blue, secondary_hue=gr.themes.colors.orange, neutral_hue=gr.themes.colors.slate, radius_size=gr.themes.sizes.radius_lg, font=[gr.themes.GoogleFont(FONT, weights=(300, 400, 500, 600)), "ui-sans-serif", "system-ui", "sans-serif"], font_mono=[gr.themes.GoogleFont(FONT_MONO), "ui-monospace", "monospace"], ) valid = set(inspect.signature(theme.set).parameters) values = {} for k, v in _SHARED.items(): if k in valid: values[k] = v if f"{k}_dark" in valid: values[f"{k}_dark"] = v for k, v in _LIGHT.items(): if k in valid: values[k] = v for k, v in _DARK.items(): if f"{k}_dark" in valid: values[f"{k}_dark"] = v elif k in valid and k not in _LIGHT: values[k] = v return theme.set(**values) # Opens in light unless the viewer chose dark before (remembered in this browser) or the URL has # ?__theme=dark. Gradio would otherwise follow the system setting, so the class is set explicitly. # The light-bulb button toggles the theme; a click handler on the document survives re-renders. THEME_JS = """ () => { const KEY = 'safetulu-theme'; const sync = (dark) => { const btn = document.getElementById('theme-toggle'); if (!btn) return; const label = dark ? 'Switch to light theme' : 'Switch to dark theme'; btn.setAttribute('aria-label', label); btn.setAttribute('title', label); btn.setAttribute('aria-pressed', String(dark)); }; const apply = (mode) => { const dark = mode === 'dark'; document.body.classList.toggle('dark', dark); document.documentElement.style.colorScheme = dark ? 'dark' : 'light'; sync(dark); }; let saved = null; try { saved = localStorage.getItem(KEY); } catch (e) {} const param = new URLSearchParams(window.location.search).get('__theme'); apply(param === 'dark' || param === 'light' ? param : (saved === 'dark' ? 'dark' : 'light')); document.addEventListener('click', (event) => { if (!event.target.closest('#theme-toggle')) return; const next = document.body.classList.contains('dark') ? 'light' : 'dark'; apply(next); try { localStorage.setItem(KEY, next); } catch (e) {} }); // "Use via API" lives in the footer; hide it there and open it from the hero button instead. const apiBtn = () => Array.from(document.querySelectorAll('footer button, footer a')) .find((el) => /use via api/i.test(el.textContent || '')); const hideFooterApi = () => { const el = apiBtn(); if (!el) return; el.style.display = 'none'; const sep = el.nextElementSibling; if (sep && /^\\s*[·•]\\s*$/.test(sep.textContent || '')) sep.style.display = 'none'; }; hideFooterApi(); new MutationObserver(hideFooterApi).observe(document.body, { childList: true, subtree: true }); document.addEventListener('click', (event) => { if (!event.target.closest('.api-docs-btn')) return; event.preventDefault(); const el = apiBtn(); if (el) el.click(); }); // The hero (and its button) can render after this runs; label it once it exists. const obs = new MutationObserver(() => { if (document.getElementById('theme-toggle')) { sync(document.body.classList.contains('dark')); obs.disconnect(); } }); obs.observe(document.body, { childList: true, subtree: true }); } """ # --------------------------------------------------------------------------- # Brand pieces # --------------------------------------------------------------------------- def _grain(rgb: str) -> str: return ( "url(\"data:image/svg+xml;utf8," "" f"" "\")" ) _GRAIN = _grain("1") # light speckle, for dark surfaces and the hero _GRAIN_DARK = _grain("0.1") # dark speckle, for the light page # Light-bulb icon (Lucide "lightbulb", ISC license). The glass fills in when the light theme is on. _BULB = ( '' ) def hero_html(repo_id: str, n_examples: int, official_pct: float) -> str: return f"""

Build your own SafeTulu

remove the Tulu 3 data that sits closest to harm, at any threshold you choose

Every Tulu 3 example is scored by how close its training gradient is to harmful SafeRLHF data. Pick a threshold and the sources to filter, see what gets removed, then download the IDs, copy a script that reproduces your selection, or push the result to your own Hub account.

🤗 Dataset API Docs for Your AI Agent
""" CSS = f""" /* ---- theme tokens: light by default, dark under .dark ---- */ :root {{ --lt-page: {PAPER}; --lt-glow-1: rgba(21,116,235,0.10); --lt-glow-2: rgba(57,176,223,0.14); --lt-glow-3: rgba(232,105,42,0.06); --lt-grain: {_GRAIN_DARK}; --lt-grain-opacity: 0.05; --lt-grain-blend: multiply; --lt-ink: {INK_NAVY}; --lt-ink-muted: {INK_MUTED}; --lt-line: {LINE_L}; --lt-label: {COBALT_TEXT}; --lt-edge: rgba(21,116,235,0.45); --lt-tile-bg: linear-gradient(160deg, rgba(21,116,235,0.08), rgba(255,255,255,0.92) 55%); --lt-tile-shadow: 0 10px 30px rgba(21,60,120,0.06); --lt-accent-num: {ORANGE_TEXT}; --lt-meta-em: {ORANGE_TEXT}; --lt-link: {COBALT_TEXT}; --lt-link-line: rgba(18,87,196,0.35); --lt-tab-active: {INK_NAVY}; --lt-plot-ink: {INK_MUTED}; --lt-plot-strong: {INK_NAVY}; --lt-plot-grid: rgba(21,60,120,0.10); --lt-plot-cutoff: rgba(11,27,58,0.6); --lt-hero-shadow: 0 24px 60px rgba(21,116,235,0.22); }} .dark {{ --lt-page: {NIGHT}; --lt-glow-1: rgba(21,116,235,0.30); --lt-glow-2: rgba(6,78,111,0.45); --lt-glow-3: rgba(5,45,120,0.55); --lt-grain: {_GRAIN}; --lt-grain-opacity: 0.07; --lt-grain-blend: soft-light; --lt-ink: {TEXT}; --lt-ink-muted: {TEXT_MUTED}; --lt-line: {LINE}; --lt-label: {CYAN}; --lt-edge: rgba(57,176,223,0.7); --lt-tile-bg: linear-gradient(160deg, rgba(21,116,235,0.16), rgba(10,22,48,0.6) 55%); --lt-tile-shadow: none; --lt-accent-num: {ORANGE_HI}; --lt-meta-em: {EMBER}; --lt-link: {CYAN}; --lt-link-line: rgba(57,176,223,0.4); --lt-tab-active: {TEXT}; --lt-plot-ink: {TEXT_MUTED}; --lt-plot-strong: {TEXT}; --lt-plot-grid: rgba(159,179,207,0.12); --lt-plot-cutoff: rgba(242,246,252,0.7); --lt-hero-shadow: none; }} /* page: soft glows and film grain over the theme's base color */ body, gradio-app {{ background: radial-gradient(1100px 620px at 8% -8%, var(--lt-glow-1), transparent 60%), radial-gradient(900px 700px at 105% 20%, var(--lt-glow-2), transparent 60%), radial-gradient(700px 500px at 60% 115%, var(--lt-glow-3), transparent 65%), var(--lt-page) !important; transition: background-color 0.25s ease; }} body::after {{ content: ""; position: fixed; inset: 0; pointer-events: none; z-index: 9999; background-image: var(--lt-grain); opacity: var(--lt-grain-opacity); mix-blend-mode: var(--lt-grain-blend); }} .gradio-container {{ width: 100% !important; max-width: 1240px !important; margin: 0 auto !important; background: transparent !important; }} footer {{ opacity: 0.55; }} /* hero: the closing card of the brand film (same in both themes) */ .hero {{ position: relative; overflow: hidden; border-radius: 24px; min-height: 280px; display: flex; align-items: center; justify-content: center; text-align: center; background: radial-gradient(520px 340px at 16% 18%, rgba(164,217,232,0.95), transparent 70%), linear-gradient(118deg, {ICE} 0%, {CYAN} 26%, #1a9be8 48%, {COBALT} 76%, #0b45cf 100%); box-shadow: var(--lt-hero-shadow); isolation: isolate; }} .hero-glow {{ position: absolute; width: 420px; height: 420px; right: 14%; top: -6%; z-index: -1; background: radial-gradient(circle at 45% 50%, rgba(242,137,76,0.95), rgba(232,105,42,0.55) 38%, rgba(196,120,200,0.25) 58%, transparent 72%); filter: blur(30px); }} .hero::after {{ content: ""; position: absolute; inset: 0; pointer-events: none; background-image: {_GRAIN}; opacity: 0.22; mix-blend-mode: overlay; }} @media (max-width: 640px) {{ .hero-glow {{ width: 260px; height: 260px; right: -18%; top: -14%; }} .hero {{ border-radius: 20px; }} }} .hero-inner {{ position: relative; padding: 44px 24px; color: #fff !important; }} .hero h1 {{ margin: 0; color: #fff !important; font-weight: 300; letter-spacing: -0.035em; line-height: 1.02; font-size: clamp(38px, 6.4vw, 76px); text-shadow: 0 2px 24px rgba(5,45,120,0.25); }} .hero h1 b, .hero h1 strong {{ font-weight: 500 !important; color: #fff !important; }} .hero p.hero-sub, .hero .hero-sub {{ margin: 14px 0 0 !important; font-size: clamp(17px, 2vw, 22px) !important; font-weight: 300 !important; color: #fff !important; text-shadow: 0 1px 14px rgba(5,45,120,0.35); }} /* light-bulb theme switch: just outside the title box, aligned with its top right corner. The wrapper leaves an equal gutter on both sides, so the box stays centered. Everything is !important because Gradio's own button and text styles can otherwise win. */ .hero-wrap {{ position: relative !important; padding: 0 60px !important; }} #theme-toggle.theme-toggle {{ position: absolute !important; top: 0 !important; right: 0 !important; z-index: 2 !important; width: 44px !important; height: 44px !important; min-width: 44px !important; min-height: 44px !important; margin: 0 !important; padding: 0 !important; line-height: 0 !important; font-size: 0 !important; display: flex !important; align-items: center !important; justify-content: center !important; border-radius: 12px !important; cursor: pointer !important; background: #ffffff !important; border: 1px solid {LINE_STRONG_L} !important; color: {INK_NAVY} !important; box-shadow: 0 6px 18px rgba(21,60,120,0.10), 0 0 0 4px rgba(255,190,110,0.0) !important; transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease !important; }} #theme-toggle.theme-toggle svg {{ display: block !important; width: 20px !important; height: 20px !important; margin: 0 !important; flex: none !important; }} #theme-toggle.theme-toggle:hover {{ border-color: {COBALT} !important; transform: translateY(-1px); box-shadow: 0 8px 22px rgba(21,60,120,0.14), 0 0 18px rgba(255,190,110,0.45) !important; }} #theme-toggle.theme-toggle:focus-visible {{ outline: 2px solid {COBALT} !important; outline-offset: 3px !important; }} #theme-toggle.theme-toggle .bulb-glass {{ fill: #ffc970 !important; transition: fill 0.2s ease; }} .dark #theme-toggle.theme-toggle {{ background: {NAVY_2} !important; border-color: {LINE_STRONG} !important; color: {TEXT} !important; box-shadow: none !important; }} .dark #theme-toggle.theme-toggle:hover {{ border-color: {ELECTRIC} !important; }} .dark #theme-toggle.theme-toggle .bulb-glass {{ fill: none !important; }} /* phones: no room beside the box, so the bulb sits inside its top right corner */ @media (max-width: 700px) {{ .hero-wrap {{ padding: 0 !important; }} #theme-toggle.theme-toggle {{ top: 12px !important; right: 12px !important; color: #fff !important; background: rgba(255,255,255,0.18) !important; border-color: rgba(255,255,255,0.5) !important; backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); box-shadow: 0 0 18px rgba(255,190,110,0.45) !important; }} .dark #theme-toggle.theme-toggle {{ background: rgba(4,11,24,0.35) !important; border-color: rgba(255,255,255,0.3) !important; box-shadow: none !important; color: #fff !important; }} .hero-inner {{ padding-top: 68px !important; }} }} .hero-meta {{ max-width: 860px; margin: 22px auto 6px; text-align: center; color: var(--lt-ink-muted); }} .hero-meta p {{ margin: 0 0 8px; font-size: 15px; line-height: 1.6; color: var(--lt-ink-muted) !important; }} .hero-meta .meta-line {{ font-size: 13px; letter-spacing: 0.01em; }} .hero-meta .meta-line span {{ margin: 0 6px; opacity: 0.6; }} .hero-meta em {{ font-style: normal; color: var(--lt-meta-em) !important; }} .hero-actions {{ display: flex; justify-content: center; gap: 14px; flex-wrap: wrap; margin: 18px 0 6px; }} .hero-meta a.hero-btn, .hero-actions .hero-btn {{ display: inline-flex !important; align-items: center; justify-content: center; gap: 8px; padding: 12px 26px !important; border-radius: 999px !important; font-size: 16px !important; font-weight: 600 !important; cursor: pointer; color: #fff !important; border: none !important; border-bottom: none !important; text-decoration: none !important; line-height: 1.2 !important; background: linear-gradient(118deg, {CYAN} 0%, {COBALT} 100%) !important; box-shadow: 0 8px 22px rgba(21,116,235,0.28); transition: transform 0.15s ease, box-shadow 0.15s ease; }} .hero-meta a.hero-btn:hover, .hero-meta a.hero-btn:focus, .hero-meta a.hero-btn:visited {{ color: #fff !important; text-decoration: none !important; border-bottom: none !important; }} .hero-meta a.hero-btn:hover {{ transform: translateY(-1px); box-shadow: 0 12px 28px rgba(21,116,235,0.36); }} .hero-meta a {{ color: var(--lt-link) !important; text-decoration: none; border-bottom: 1px solid var(--lt-link-line); }} /* stat tiles */ .tiles {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }} .tile {{ position: relative; overflow: hidden; border: 1px solid var(--lt-line); border-radius: 16px; padding: 16px 18px; background: var(--lt-tile-bg); box-shadow: var(--lt-tile-shadow); }} .tile::before {{ content: ""; position: absolute; left: 18px; right: 18px; top: 0; height: 1px; background: linear-gradient(90deg, transparent, var(--lt-edge), transparent); }} .tile-label {{ font-size: 11px; font-weight: 500; color: var(--lt-label); text-transform: uppercase; letter-spacing: 0.12em; }} .tile-value {{ font-size: 30px; font-weight: 400; letter-spacing: -0.02em; color: var(--lt-ink); font-variant-numeric: tabular-nums; margin-top: 6px; }} .tile-sub {{ font-size: 12px; color: var(--lt-ink-muted); margin-top: 4px; line-height: 1.4; }} .tile.accent .tile-value {{ color: var(--lt-accent-num); }} /* section headings above the charts */ .section-title p {{ margin: 6px 0 -4px !important; font-size: 13px !important; font-weight: 500 !important; color: var(--lt-ink-muted) !important; text-transform: uppercase; letter-spacing: 0.12em; }} /* tabs */ button[role="tab"] {{ color: var(--lt-ink-muted) !important; font-weight: 500; }} button[role="tab"][aria-selected="true"] {{ color: var(--lt-tab-active) !important; border-color: {ORANGE} !important; }} /* PLOT CSS: axis text, gridlines and the cutoff line follow the theme without re-rendering the charts. Hover tooltips stay navy in both themes. */ .js-plotly-plot .cartesianlayer text, .js-plotly-plot .infolayer text {{ fill: var(--lt-plot-ink) !important; }} .js-plotly-plot .infolayer .annotation text {{ fill: var(--lt-plot-strong) !important; }} .js-plotly-plot .gridlayer path, .js-plotly-plot path.xlines-above, .js-plotly-plot path.ylines-above {{ stroke: var(--lt-plot-grid) !important; }} .js-plotly-plot .shapelayer path {{ stroke: var(--lt-plot-cutoff) !important; }} """