preload_from_hub bakes the HF cache into the image owned by the build
user, so any runtime download of a file it missed dies with EACCES
(xet_get, os error 13) — first in the GPU worker, then at startup once
the module-level load landed. app.py now points HF_HOME/HF_XET_CACHE/
TMPDIR at an app-local writable cache before anything imports
huggingface_hub, and models download at startup (Nemotron 4B, ~8GB,
inside the 45min startup window) or first use (Mellum/MiniCPM in-task,
the path v1 proved).
Also renames the app to “Ready to Submit?” across the hero, page title,
README and reviewer persona.
The 4B (3.97B params, ~8GB bf16) keeps the sponsor:nvidia story without
the 30B's 63GB footprint. It loads at module level on the Space — the
fine-tuner-proven ZeroGPU pattern; in-task loading made the worker
re-download weights onto a read-only fs (the Permission denied) — via
the native nemotron_h class. Mellum/MiniCPM stay as lazy in-task loads;
all three fit the default slice, so the xlarge request goes away.
Startup gets a 45min timeout for the first cold boot.
Pin kernels to transformers 5.11's supported range
kernels is an optional transformers extra, so pip ignored the declared
kernels>=0.12,<0.13 constraint and installed 0.15.x, whose
LayerRepository requires a revision/version and crashes hub_kernels at
import — taking every model load down with it.
Bring Nemotron 3 Nano back as the default reviewer
Loads through transformers' native nemotron_h class (>=5.4) instead of
the repo custom code, which hard-requires the mamba-ssm pip package;
the kernels package fetches prebuilt mamba/conv kernels from the Hub
at runtime with a torch fallback. eos ids are now the union of the
tokenizer, generation_config and <|im_end|> so each model stops at its
own turn end (Nemotron needs [2, 11]). GPU window widened to 420s for
the 63GB first load; weights preloaded at build. README re-claims
sponsor:nvidia.
MiniCPM5-1B was parroting the rules digest and the raw facts JSON back
instead of reviewing. The briefing is now a compact cheat-sheet plus
short labelled bullets (no JSON), with the task restated at the very
end where small models attend, an explicit do-not-quote instruction,
and slightly tamer sampling (temp 0.5, top_p 0.9, rep penalty 1.05).
Render markdown tables and stop truncating reviews
Reviews that used pipe tables showed raw pipes (python-markdown needs
the tables extension) and longer reviews hit the 1200-token cap
mid-sentence. Enable the extension with matching pastel table styles,
raise the cap to 2048, and ask the reviewer to stay under ~450 words
so it concludes cleanly.
Fix space dropdown swap target, add next-step arrow cue
The space select inherited the form's hx-target=#results, so the org
options fragment rendered into the results area instead of the select;
hx-target=this scopes the swap correctly.
After step 1 lands, a floating bouncing arrow now points the user to
the review block — click scrolls to it, and an IntersectionObserver
removes the cue once step 2 is in view.
The review opened by celebrating an achievement claim the checklist had
flagged, then corrected itself two sections later. Add an explicit
constraint: only clean passes belong in the praise.
Fix dropped review stream and runaway Mellum generation
gr.Server infers an api endpoint's outputs from the return annotation;
review_api had none, so it registered with zero outputs and every
streamed chunk was discarded. Annotate Iterator[str].
Mellum2's generation_config carries eos_token_id=0 (its bos) while the
chat template ends turns with <|im_end|>, so generation never stopped
and the model invented a follow-up user turn and answered it. Pass the
real end-of-turn ids to generate explicitly.
Claim achievement:offbrand and preload reviewer weights
The gr.Server + HTMX custom UI is the off-brand story; preload_from_hub
warms both reviewer models onto disk at build time so first reviews
skip the download.
Rewrite app on gr.Server with a friendly reviewer voice
FastAPI routes serve the shell, the org-spaces options fragment, and
the deterministic check block; the review runs as a Gradio-queued
endpoint streaming rendered-markdown HTML chunks, with a short-lived
eval cache so the review reuses the facts the check step computed.
The system prompt becomes a warm camp-counselor voice — celebrates
what passes, frames problems as next steps — with the same hard
grounding constraints.
Custom gr.Server shell instead of stock Gradio: vendored htmx +
gradio-client (from vivamais), self-hosted Archivo and Spline Sans
Mono, and a calm pastel re-skin of the Build Small woodblock-press
language (paper grain, mono eyebrows, dashed-ring stamps, soft press
shadows). bridge.js opens the Gradio-queue stream for the review block
and swaps chunks via htmx; the htmx indicator drives the spinner.
Add HTML fragment presenters with field-guide stamp badges
presenters.py renders every fragment the frontend swaps in: verdict
header, friendly checklist rows (all good / worth a look / needs
fixing), tag chips and opportunities as mini stamp badges, the
streaming review shell, and the always-on double-check disclaimer.
icons.py inlines the same Lucide glyphs the field guide site maps to
each track and badge; guide.py carries that tag-to-glyph mapping.
Verify every claimed tag against evidence, incl. Codex commits
Commit history is now scanned for Codex attribution (sponsor:openai
passes with examples or warns when tagged without evidence). All six
achievement tags get the same verify-or-warn treatment: offgrid vs
external API hosts, welltuned vs own/fine-tune models, offbrand vs
custom-UI files, llama vs llama.cpp/GGUF, sharing vs linked datasets,
fieldnotes vs write-up links. Evidence without a claim surfaces as an
opportunity.
Disable hybrid-reasoning thinking in the reviewer
MiniCPM5 spent the whole token budget inside an unclosed <think> block,
so the review ended as the placeholder. Pass enable_thinking=False to
the chat template, and on final yield unwrap an unclosed think block
instead of showing the placeholder.
Fix generate call for dict-returning apply_chat_template
Newer transformers returns a BatchEncoding from apply_chat_template,
which crashed model.generate(input_ids=...) in the streamer thread and
left the GPU task hanging until abort. Request return_dict explicitly
and splat the encoding into generate.
The Nemotron 3 Nano 30B default was ~63GB bf16 — heavy for the ZeroGPU
slice and slow to first-load. Mellum 2 12B-A2.5B Instruct becomes the
default, MiniCPM5 1B stays as the fast option. README swaps the
sponsor:nvidia tag for sponsor:openbmb to match the models actually used.
Make the Space self-compliant as a hackathon entry
Canonical track:backyard and sponsor:nvidia tags, linked models, a
write-up of the idea and tech, and placeholders for the demo video and
social post links.
Add Gradio UI wiring checks to the reviewer model
Model + space pickers (org spaces fetched live, free text allowed),
instant verdict/checklist/opportunities from the deterministic layer,
then the streamed LLM review.
Three curated reviewer models (Nemotron 3 Nano 30B-A3B default, Mellum 2
12B, MiniCPM5 1B), lazily loaded with one resident at a time since the
Nemotron default is ~63GB bf16 on the H200 slice. Streams the review,
hides Nemotron's <think> trace, and degrades to a notice without CUDA.
checks.py evaluates a Space in the build-small-hackathon org against the
six official entry rules (Gradio app, track tags, demo video, social post,
write-up, <32B models) using only the public HF Hub API. guide.py embeds
the canonical tag ids and rules digest extracted from the field guide's
own source (build-small-hackathon/field-guide).