Spaces:
Running on Zero
Running on Zero
GUI-Decisions Space
Browse files- ambient.css +48 -0
- app.py +41 -5
- intro.html +14 -0
ambient.css
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* The Ambient design system (vaan_db/site/public/assets/css/styles.css), the same tokens as the blog's
|
| 2 |
+
figures (systemone/blog/diagrams/_head.html): cream canvas, dark-brown ink, one orange accent. Orange is
|
| 3 |
+
the slot read, dark brown the base model writing its answer, taupe neutral structure. */
|
| 4 |
+
:root {
|
| 5 |
+
--bg:#fdf6f1; --bg-2:#f8ece4; --panel:#ffffff; --panel-2:#fbf1ea;
|
| 6 |
+
--ink-panel:#1c100b; --ink-panel-2:#2a170e; --on-ink:#fbeee6; --on-ink-muted:#bfa89c;
|
| 7 |
+
--ink:#1a110d; --ink2:#6b5850; --ink3:#9a877e; --rule:rgba(58,28,12,0.11); --rule-2:rgba(58,28,12,0.06); --dot:rgba(58,28,12,0.07);
|
| 8 |
+
--shadow:0 1px 2px rgba(58,28,12,0.05), 0 12px 32px -14px rgba(58,28,12,0.18);
|
| 9 |
+
--fast:#ff5a14; --fast-2:#ff7a2e; --fast-soft:#ffe5d8; /* ours: the slot read */
|
| 10 |
+
--slow:#3a2013; --slow-soft:#efe2d9; /* the baseline: writing */
|
| 11 |
+
--accent:#a3836f; --accent-soft:#f3e7df; /* neutral structure */
|
| 12 |
+
}
|
| 13 |
+
body, gradio-app, .gradio-container {
|
| 14 |
+
background:radial-gradient(var(--dot) 1.2px, transparent 1.2px) -12px -12px / 24px 24px, var(--bg) !important;
|
| 15 |
+
color:var(--ink); -webkit-font-smoothing:antialiased;
|
| 16 |
+
}
|
| 17 |
+
.gradio-container { max-width:1180px !important; margin:0 auto !important; }
|
| 18 |
+
|
| 19 |
+
/* header, as the site's sections: orange eyebrow, tight display title, muted lede */
|
| 20 |
+
.amb-head { padding:26px 4px 6px; }
|
| 21 |
+
.amb-head .eyebrow { font:600 11.5px "Inter",sans-serif; letter-spacing:.26em; text-transform:uppercase; color:var(--fast); margin:0 0 10px; }
|
| 22 |
+
.amb-head .title { font:800 34px/1.08 "Inter Tight","Inter",sans-serif; letter-spacing:-.03em; word-spacing:.04em; margin:0 0 12px; color:var(--ink); text-wrap:balance; }
|
| 23 |
+
.amb-head .title .fast { color:var(--fast); }
|
| 24 |
+
.amb-head .lede { color:var(--ink2); font-size:15.5px; line-height:1.6; margin:0 0 12px; max-width:54rem; }
|
| 25 |
+
.amb-head .links { font:500 12px "JetBrains Mono",monospace; letter-spacing:.06em; color:var(--ink3); margin:0; }
|
| 26 |
+
.amb-head .links a { color:var(--ink); text-decoration:none; border-bottom:1px solid var(--rule); }
|
| 27 |
+
.amb-head .links a:hover { color:var(--fast); border-color:var(--fast); }
|
| 28 |
+
.amb-note { color:var(--ink3); font-size:12.5px; line-height:1.55; margin:6px 4px 0; }
|
| 29 |
+
.amb-legend { display:flex; gap:18px; flex-wrap:wrap; font:500 12px "JetBrains Mono",monospace; color:var(--ink2); margin:2px 4px 0; }
|
| 30 |
+
.amb-legend i { display:inline-block; width:10px; height:10px; border-radius:50%; margin-right:6px; vertical-align:-1px; }
|
| 31 |
+
|
| 32 |
+
/* results: the read in a dark terminal panel, like the figures' .term */
|
| 33 |
+
.prose pre, .prose code { font-family:"JetBrains Mono",ui-monospace,monospace !important; }
|
| 34 |
+
.prose pre { background:var(--ink-panel) !important; color:var(--on-ink) !important; border-radius:12px !important;
|
| 35 |
+
border:0 !important; box-shadow:0 18px 36px -22px rgba(42,23,14,.6); }
|
| 36 |
+
.prose pre code { background:transparent !important; color:var(--on-ink) !important; }
|
| 37 |
+
.prose :not(pre) > code { background:var(--accent-soft) !important; color:var(--ink) !important; border-radius:6px; padding:1px 5px; }
|
| 38 |
+
.prose h3 { font:700 17px "Inter Tight","Inter",sans-serif !important; letter-spacing:-.02em; }
|
| 39 |
+
.prose strong { color:var(--ink); }
|
| 40 |
+
.prose table { font-variant-numeric:tabular-nums; border-collapse:collapse; background:var(--panel); }
|
| 41 |
+
.prose table, .prose th, .prose td { border:1px solid var(--rule) !important; }
|
| 42 |
+
.prose h2 { font:800 22px "Inter Tight","Inter",sans-serif !important; letter-spacing:-.03em; }
|
| 43 |
+
.prose th { color:var(--ink3); font:500 11px "JetBrains Mono",monospace !important; letter-spacing:.12em; text-transform:uppercase; }
|
| 44 |
+
|
| 45 |
+
/* tabs: an orange rule under the selected one */
|
| 46 |
+
button[role="tab"] { font:600 14px "Inter",sans-serif !important; color:var(--ink2) !important; }
|
| 47 |
+
button[role="tab"][aria-selected="true"] { color:var(--ink) !important; border-color:var(--fast) !important; }
|
| 48 |
+
button[role="tab"][aria-selected="true"]::after { background:var(--fast) !important; }
|
app.py
CHANGED
|
@@ -18,7 +18,7 @@ from slotread.numbers import unwarp
|
|
| 18 |
|
| 19 |
BASE = os.environ.get("BASE", "google/gemma-4-31B-it")
|
| 20 |
ADAPTER = os.environ.get("ADAPTER", "infinitylogesh/GUI-Decisions-31B-LoRA")
|
| 21 |
-
SLOT, VANILLA = (
|
| 22 |
MAX_PX = 3840 * 2160
|
| 23 |
WARM = set() # functions that have run once in this process: the first call includes CUDA warm-up
|
| 24 |
|
|
@@ -109,7 +109,7 @@ def decide(image, instruction, previous, compare):
|
|
| 109 |
md += (f"\n\n### Base model writing JSON: `{describe(va, vd, vtext)}`\n\n"
|
| 110 |
f"Generating its answer took **{vms:.0f} ms** (Gemma 4's native `\"point\": [y, x]` format):\n\n"
|
| 111 |
f"```\n{raw.strip()[:400]}\n```\n\n"
|
| 112 |
-
"
|
| 113 |
label = {k: float(p) for k, p in zip(ACTIONS, probs(lp["action"]))}
|
| 114 |
return out.convert("RGB"), md, label
|
| 115 |
|
|
@@ -137,11 +137,15 @@ def box(image, description):
|
|
| 137 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 138 |
ex = lambda f: os.path.join(HERE, "examples", f)
|
| 139 |
rows = lambda f: [l.split("\t") for l in open(ex(f)).read().splitlines() if l.strip()] # tab-separated examples
|
| 140 |
-
INTRO = open(os.path.join(HERE, "intro.
|
|
|
|
| 141 |
HOW = open(os.path.join(HERE, "how.md")).read()
|
| 142 |
|
| 143 |
with gr.Blocks(title="GUI-Decisions") as demo:
|
| 144 |
-
gr.
|
|
|
|
|
|
|
|
|
|
| 145 |
with gr.Tab("Computer use"):
|
| 146 |
with gr.Row():
|
| 147 |
with gr.Column():
|
|
@@ -152,6 +156,8 @@ with gr.Blocks(title="GUI-Decisions") as demo:
|
|
| 152 |
go = gr.Button("Decide", variant="primary")
|
| 153 |
with gr.Column():
|
| 154 |
out_img = gr.Image(label="Decision")
|
|
|
|
|
|
|
| 155 |
out_md = gr.Markdown()
|
| 156 |
out_lab = gr.Label(label="action slot", num_top_classes=4)
|
| 157 |
go.click(decide, [img, ins, prev, cmp], [out_img, out_md, out_lab])
|
|
@@ -170,5 +176,35 @@ with gr.Blocks(title="GUI-Decisions") as demo:
|
|
| 170 |
with gr.Tab("How it works"):
|
| 171 |
gr.Markdown(HOW)
|
| 172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 173 |
if __name__ == "__main__":
|
| 174 |
-
demo.queue().launch()
|
|
|
|
| 18 |
|
| 19 |
BASE = os.environ.get("BASE", "google/gemma-4-31B-it")
|
| 20 |
ADAPTER = os.environ.get("ADAPTER", "infinitylogesh/GUI-Decisions-31B-LoRA")
|
| 21 |
+
SLOT, VANILLA = (255, 90, 20), (58, 32, 19) # the Ambient theme: orange the slot read, dark brown the base model writing
|
| 22 |
MAX_PX = 3840 * 2160
|
| 23 |
WARM = set() # functions that have run once in this process: the first call includes CUDA warm-up
|
| 24 |
|
|
|
|
| 109 |
md += (f"\n\n### Base model writing JSON: `{describe(va, vd, vtext)}`\n\n"
|
| 110 |
f"Generating its answer took **{vms:.0f} ms** (Gemma 4's native `\"point\": [y, x]` format):\n\n"
|
| 111 |
f"```\n{raw.strip()[:400]}\n```\n\n"
|
| 112 |
+
"Orange: the slot read, with its x × y distribution as the heatmap. Dark brown: the base model's point.")
|
| 113 |
label = {k: float(p) for k, p in zip(ACTIONS, probs(lp["action"]))}
|
| 114 |
return out.convert("RGB"), md, label
|
| 115 |
|
|
|
|
| 137 |
HERE = os.path.dirname(os.path.abspath(__file__))
|
| 138 |
ex = lambda f: os.path.join(HERE, "examples", f)
|
| 139 |
rows = lambda f: [l.split("\t") for l in open(ex(f)).read().splitlines() if l.strip()] # tab-separated examples
|
| 140 |
+
INTRO = open(os.path.join(HERE, "intro.html")).read()
|
| 141 |
+
CSS = open(os.path.join(HERE, "ambient.css")).read()
|
| 142 |
HOW = open(os.path.join(HERE, "how.md")).read()
|
| 143 |
|
| 144 |
with gr.Blocks(title="GUI-Decisions") as demo:
|
| 145 |
+
gr.HTML(INTRO)
|
| 146 |
+
gr.HTML('<p class="amb-note">Timings here are this Space\'s shared GPU running bf16 in plain transformers. '
|
| 147 |
+
'Served on vLLM (NVFP4, one RTX PRO 6000) a step takes ~150 ms, against ~470–550 ms for the same model '
|
| 148 |
+
'writing its native JSON.</p>')
|
| 149 |
with gr.Tab("Computer use"):
|
| 150 |
with gr.Row():
|
| 151 |
with gr.Column():
|
|
|
|
| 156 |
go = gr.Button("Decide", variant="primary")
|
| 157 |
with gr.Column():
|
| 158 |
out_img = gr.Image(label="Decision")
|
| 159 |
+
gr.HTML('<div class="amb-legend"><span><i style="background:#ff5a14"></i>slot read · P(x) × P(y)</span>'
|
| 160 |
+
'<span><i style="background:#3a2013"></i>base model writing JSON</span></div>')
|
| 161 |
out_md = gr.Markdown()
|
| 162 |
out_lab = gr.Label(label="action slot", num_top_classes=4)
|
| 163 |
go.click(decide, [img, ins, prev, cmp], [out_img, out_md, out_lab])
|
|
|
|
| 176 |
with gr.Tab("How it works"):
|
| 177 |
gr.Markdown(HOW)
|
| 178 |
|
| 179 |
+
# the Ambient theme: Inter / Inter Tight / JetBrains Mono, cream canvas, dark-brown ink, orange accent (light only)
|
| 180 |
+
ORANGE = gr.themes.Color(c50="#fff4ee", c100="#ffe5d8", c200="#ffc9ad", c300="#ffa77d", c400="#ff7a2e", c500="#ff5a14",
|
| 181 |
+
c600="#e64a08", c700="#bf3c06", c800="#8f2e07", c900="#5e1f06", c950="#3a1304")
|
| 182 |
+
WARM = gr.themes.Color(c50="#fdf6f1", c100="#f8ece4", c200="#efe2d9", c300="#d9c6ba", c400="#9a877e", c500="#6b5850",
|
| 183 |
+
c600="#4f3d35", c700="#3a2013", c800="#2a170e", c900="#1c100b", c950="#1a110d")
|
| 184 |
+
LIGHT = dict(
|
| 185 |
+
body_background_fill="#fdf6f1", body_text_color="#1a110d", body_text_color_subdued="#6b5850",
|
| 186 |
+
background_fill_primary="#ffffff", background_fill_secondary="#fbf1ea",
|
| 187 |
+
block_background_fill="#ffffff", block_border_color="rgba(58,28,12,0.11)", block_border_width="1px",
|
| 188 |
+
block_label_background_fill="#fbf1ea", block_label_text_color="#6b5850", block_title_text_color="#6b5850",
|
| 189 |
+
block_shadow="0 1px 2px rgba(58,28,12,0.05), 0 12px 32px -14px rgba(58,28,12,0.18)",
|
| 190 |
+
border_color_primary="rgba(58,28,12,0.11)", border_color_accent="#ff5a14",
|
| 191 |
+
input_background_fill="#fdf6f1", input_border_color="rgba(58,28,12,0.11)", input_border_color_focus="#ff7a2e",
|
| 192 |
+
button_primary_background_fill="#ff5a14", button_primary_background_fill_hover="#ff7a2e",
|
| 193 |
+
button_primary_text_color="#ffffff", button_primary_border_color="#ff5a14",
|
| 194 |
+
button_secondary_background_fill="#ffffff", button_secondary_background_fill_hover="#fbf1ea",
|
| 195 |
+
button_secondary_text_color="#1a110d", button_secondary_border_color="rgba(58,28,12,0.11)",
|
| 196 |
+
color_accent="#ff5a14", color_accent_soft="#ffe5d8", link_text_color="#ff5a14", link_text_color_hover="#e64a08",
|
| 197 |
+
checkbox_background_color_selected="#ff5a14", checkbox_border_color_selected="#ff5a14", slider_color="#ff5a14",
|
| 198 |
+
loader_color="#ff5a14", code_background_fill="#f3e7df", table_even_background_fill="#ffffff",
|
| 199 |
+
table_odd_background_fill="#fbf1ea", table_border_color="rgba(58,28,12,0.11)",
|
| 200 |
+
)
|
| 201 |
+
THEME = gr.themes.Base(primary_hue=ORANGE, secondary_hue=WARM, neutral_hue=WARM, radius_size=gr.themes.sizes.radius_lg,
|
| 202 |
+
font=[gr.themes.GoogleFont("Inter"), "system-ui", "sans-serif"],
|
| 203 |
+
font_mono=[gr.themes.GoogleFont("JetBrains Mono"), "ui-monospace", "monospace"])
|
| 204 |
+
THEME.set(block_radius="16px", **LIGHT, **{k + "_dark": v for k, v in LIGHT.items() if hasattr(THEME, k + "_dark")})
|
| 205 |
+
HEAD = ('<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700'
|
| 206 |
+
'&family=Inter+Tight:wght@700;800&family=JetBrains+Mono:wght@400;500;600&display=swap">')
|
| 207 |
+
LIGHT_ONLY = "() => { document.body.classList.remove('dark'); document.documentElement.classList.remove('dark'); }"
|
| 208 |
+
|
| 209 |
if __name__ == "__main__":
|
| 210 |
+
demo.queue().launch(theme=THEME, css=CSS, head=HEAD, js=LIGHT_ONLY)
|
intro.html
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<div class="amb-head">
|
| 2 |
+
<p class="eyebrow">Slot reading · GUI-Decisions</p>
|
| 3 |
+
<p class="title">Computer-use steps, <span class="fast">read</span> in one forward pass</p>
|
| 4 |
+
<p class="lede">Give it a screenshot and an instruction. Instead of <em>writing</em> <code>{"action": "click", "point": [412, 288]}</code>
|
| 5 |
+
token by token, the model fills a fixed template of slots, and every answer is read from one forward pass: the action, and the
|
| 6 |
+
point as a 256-bin distribution over x and over y. Nothing is decoded; the heatmap is that distribution.</p>
|
| 7 |
+
<p class="links">
|
| 8 |
+
<a href="https://logeshumapathi.com/blog/2026/10/06/slot-reading.html">blog post</a> ·
|
| 9 |
+
<a href="https://github.com/infinitylogesh/gui-decisions">code</a> ·
|
| 10 |
+
<a href="https://huggingface.co/infinitylogesh/GUI-Decisions-31B-LoRA">GUI-Decisions-31B-LoRA</a> ·
|
| 11 |
+
<a href="https://huggingface.co/infinitylogesh/GUI-Decisions-31B">GUI-Decisions-31B</a> ·
|
| 12 |
+
<a href="https://huggingface.co/infinitylogesh/GUI-Decisions-2B-LoRA">GUI-Decisions-2B-LoRA</a>
|
| 13 |
+
</p>
|
| 14 |
+
</div>
|