CoolFace
Apppublic

build-small-hackathon/microfactory-lab

sourceHugging Facemitupdated 3mo agoView on Hugging Face
2likes
app.py1254 linesDownload Raw Back to root
1"""The Chief Engineer — Gradio app (four workspaces: Build, Slice, Print, Review).2 3STUDIO: define the job (part + material + simulated room). BUILD: slice + the4engineer's pre-flight read (precedent, risks, Spine veto, second opinion). PRINT:5run the closed loop (quality compounds fail->clean, the Inspector grades each run,6then log a real print). REVIEW: the compounding made visible (ledger + verdict).7 8UI follows the walkthrough spec: no emojis (custom inline-SVG icons only), one9consolidated custom loader then progressive reveal, a small primary action in the10same top-right spot on every tab with a persistent Reset, grouped contained blocks,11mirrored header/footer.12 13Local-first. Real Ollama calls (gemma4:e4b), deterministic fallback so the demo14never crashes. Run: `ollama serve` + `make run` (= `uv run python app.py`).15"""16 17from __future__ import annotations18 19import os20import random21import time22import uuid23from pathlib import Path24 25import gradio as gr26 27try:28    import spaces  # ZeroGPU @spaces.GPU decorator — HF-provided on the Space.29except ImportError:  # not installed locally (base env / offline); decorator no-ops.30    class _SpacesShim:31        @staticmethod32        def GPU(fn=None, **_kw):33            return fn if fn is not None else (lambda f: f)34 35    spaces = _SpacesShim()  # type: ignore36 37from core import deliberation_log38from core import field_log39from core import inspector40from core import llm41from core import seed_lessons42from core import signups43from core.theme import (44    THEME, CSS, rule, command_bar, footer_bar, inspector_panel, icon, loader, tab_intro,45    CLOCK_JS,46)47from core.widgets import virtual_printer_html, layer_image, SCRUB_LAYERS, VP_HEAD48from core.chief_engineer import advise49from core.ledger import LedgerManager50from core.models import Advice, BED_POSITIONS, Environment, Job, MATERIALS, PrintSettings51from core.nodes import render_node_cards52from core.reflect import reflect_on_job53from core.spine import SpineValidator54from learn.loop import run_iteration, SessionResult55from learn.policy import LearnedPolicy, cell_key56from core.viewer import (57    GEO_READS,58    benchy_mesh,59    gcode_panel_html,60    generate_primitive,61    infer_geometry,62    iteration_log_html,63    placement_callout,64    policy_cell_html,65    precedent_eval_html,66    quality_curve_html,67    risk_callouts_html,68    settings_panel_html,69    steepest_overhang_hint,70)71 72try:73    from ingest.distill import reference_block74except Exception:  # ingestion is optional / removable75    def reference_block(material):  # type: ignore76        return []77 78LEDGER = LedgerManager()79SPINE = SpineValidator()80POLICY = LearnedPolicy()81_loaded = seed_lessons.ensure_seeded(LEDGER)82 83# On the Space (backend=zerogpu), import the inference module at startup so its84# @spaces.GPU function (core/llm_zerogpu._generate) is registered — ZeroGPU requires85# at least one detected GPU function, and build_job is deliberately no longer one.86if __import__("os").environ.get("CHIEF_ENGINEER_BACKEND") == "zerogpu":87    try:88        import core.llm_zerogpu  # noqa: F401  (registers @spaces.GPU on import)89        import core.llm_zerogpu_lora  # noqa: F401  (LoRA-aware variant)90    except Exception:91        pass92 93# Model switcher: maps UI labels to backend configuration94# Space-compatible backends only (ZeroGPU + Modal). Local Ollama options removed —95# they only work in local development, not on the Space.96MODEL_OPTIONS = [97    "LoRA v3 (QAT E4B)",98    "LoRA v2 (Standard E4B)",99    "Base Gemma 4 E4B (ZeroGPU)",100    "Modal API (remote)",101]102 103MODEL_LORA_MAP = {104    "LoRA v2 (Standard E4B)": "kylebrodeur/microfactory-node-lora-v2",105    "LoRA v3 (QAT E4B)": "kylebrodeur/microfactory-node-lora-v3-qat",106}107 108def _apply_model_choice(model_choice: str):109    """Set environment variables so the next advise() call uses the chosen backend."""110    if model_choice == "Base Gemma 4 E4B (ZeroGPU)":111        os.environ.pop("CHIEF_ENGINEER_LORA_REPO", None)112        os.environ["CHIEF_ENGINEER_BACKEND"] = "zerogpu"113    elif model_choice in MODEL_LORA_MAP:114        os.environ["CHIEF_ENGINEER_LORA_REPO"] = MODEL_LORA_MAP[model_choice]115        os.environ["CHIEF_ENGINEER_BACKEND"] = "zerogpu"116    elif model_choice == "Modal API (remote)":117        os.environ.pop("CHIEF_ENGINEER_LORA_REPO", None)118        os.environ["CHIEF_ENGINEER_BACKEND"] = "modal"119    # No module reload needed: core.llm reads the backend + adapter env dynamically120    # (llm._backend() / CHIEF_ENGINEER_LORA_REPO), so the switch takes effect on the121    # next call. (reload(__import__("core.llm")) reloaded the package, not the submodule,122    # so it was a no-op for routing anyway.)123 124# Default to the winning LoRA v3 (QAT) model — best loss, best quantization quality125_apply_model_choice("LoRA v3 (QAT E4B)")126 127# Astrometrics OS visual layer lives in core/theme.py (THEME + CSS + helpers) so128# the Off-Brand skin stays a single removable module. See ../DESIGN.md.129 130PRINTER = "Creality Ender 3 V2"131 132 133def _delib_ctx(job, env) -> dict:134    """Context columns shared by every deliberation-log turn for a job."""135    return {"material": job.material, "geometry": job.geometry_type,136            "bed_position": job.bed_position, "env_temp": env.temp, "env_humidity": env.humidity}137_SCROLL_TOP = "() => { window.scrollTo({ top: 0, behavior: 'smooth' }); }"138 139 140def ledger_html() -> str:141    c = LEDGER.count()142    entries = LEDGER.all()[-8:][::-1]143    rows = []144    for e in entries:145        tag = "SEED" if e.source == "seed" else "EARNED"146        col = "var(--ao-outline)" if e.source == "seed" else "var(--ao-green)"147        rows.append(148            f"<div style='border-left:3px solid {col};background:var(--ao-surface);padding:6px 10px;"149            f"margin:4px 0;font-family:ui-monospace,monospace;font-size:11px;'>"150            f"<span style='color:{col};font-weight:700;'>{tag}</span> "151            f"<span style='color:var(--ao-orange);'>{e.material}/{e.geometry_type}</span> "152            f"<span style='color:var(--ao-outline);'>@ {e.env_temp:.0f}°C/{e.env_humidity:.0f}% → {e.outcome}</span>"153            f"<div style='color:var(--ao-text);'>{e.lesson}</div></div>"154        )155    head = (156        f"<div style='color:var(--ao-orange);font-family:ui-monospace,monospace;'>"157        f"LEDGER · {c['total']} lessons ({c['seed']} seed · {c['earned']} earned)</div>"158    )159    return head + "".join(rows)160 161 162def studio_log_html() -> str:163    """Job log near the top of Studio: what is stored, and where (studio-14/17)."""164    c = LEDGER.count()165    return (166        "<div class='ce-card'>"167        f"<div style='color:var(--ao-orange);font-weight:700;letter-spacing:1.5px;font-size:11px;'>"168        f"{icon('book')} JOB LOG · {c['total']} LESSONS "169        f"<span style='color:var(--ao-outline);font-weight:400;'>({c['seed']} seed · {c['earned']} earned)</span></div>"170        "<div class='ce-sub' style='margin-top:4px;'>Every build, print, and recorded outcome is stored "171        "to <b>data/lessons.jsonl</b> (durable) and the learned policy to <b>data/policy.json</b>. "172        "This session's runs append live; <b>Reset to Baseline</b> restores the curated seed + ingested set.</div>"173        "</div>"174    )175 176 177def reset_learnings():178    """Reset the live ledger + learned policy to the curated baseline (seed + ingested),179    clearing only this session's runtime lessons and the learned policy file. Does NOT180    touch: seed_lessons.jsonl, references.jsonl, HF trace datasets (ledger/deliberation/181    field-log), or any recorded Space logs. Also clears the loaded part and hides the182    result groups so the next run starts from a clean LOAD tab."""183    removed = LEDGER.reset_to_baseline()184    POLICY.reset()185    gr.Info(f"Reset to baseline — cleared {removed} runtime lesson(s) + the learned policy. "186            f"Seed/ingested traces and HF logs were not touched.")187    return (188        ledger_html(),                                                   # ledger_panel189        render_node_cards(Environment(temp=22, humidity=45)),            # node_cards190        "<div class='ce-sub'>Reset to baseline. Run the Print loop for a fresh verdict.</div>",  # review_summary191        "", "", "",                                                      # p_curve, p_policy, p_log192        "",                                                              # p_headline193        "",                                                              # outcome_panel194        gr.update(visible=False),                                        # print results_group (re-hide)195        "",                                                              # real_log_msg196        studio_log_html(),                                              # studio_log197        {"geometry": None, "mesh": None, "label": None, "read": None}, # part state198        gr.update(value=None, visible=False),                           # model3d199        gr.update(visible=True),                                         # part_placeholder200        "",                                                              # part_status201        gr.update(visible=False),                                        # build_results202        gr.update(visible=False),                                        # read_results203        gr.Tabs(selected="studio"),                                      # tabs back to LOAD204        review_record_html(None),                                        # review_record (clear)205    )206 207 208def _viewer_placeholder() -> str:209    """Custom empty-state for the blank part/model viewer (no generic Gradio canvas)."""210    return (211        "<div class='ce-viewer'>"212        f"<div class='ce-viewer-ico'>{icon('layers', 40)}</div>"213        "<div class='ce-viewer-title'>NO PART LOADED</div>"214        "<div class='ce-viewer-copy'>Define the part, set the material and the room, then "215        "<b>SLICE</b> (Build) and <b>PRINT</b>. You give it the part, the material, and the room; "216        "it infers what kind of part this is.</div>"217        "<div class='ce-viewer-hint'>quick-load Benchy · drop a mesh · or generate a primitive →</div>"218        "</div>"219    )220 221 222def _set_part(geometry: str, mesh, label: str, read: str | None = None):223    """Shared preview update → (part_state, model3d, status, placeholder). The user never224    picks the class — the engineer infers it from the mesh (see infer_geometry). Loading a225    part reveals the 3D model and hides the custom placeholder."""226    read = read or GEO_READS.get(geometry, geometry)227    return (228        {"geometry": geometry, "mesh": mesh, "label": label, "read": read},229        gr.update(value=mesh, visible=True),230        f"ACTIVE PART · **{label}** · the engineer reads this as *{read}* → reasons about `{geometry}`",231        gr.update(visible=False),232    )233 234 235def build_start(part):236    """Instant feedback when BUILD is clicked: jump to Build, reveal the slicer +237    virtual print preview immediately, and show the read panel in a loading state238    while the model runs. No part → stay put."""239    if not (part and part.get("geometry")):240        gr.Warning("Load a part on the BUILD tab first — quick-load Benchy, generate a primitive, or drop a mesh.")241        return (gr.update(),) * 13242    mesh = part.get("mesh")243    label = part.get("label") or "PART"244    vp_html = virtual_printer_html(mesh, caption=f"{label} · virtual print preview")245    layer_img = layer_image(mesh, 1)246    return (247        gr.Tabs(selected="build"),                    # tabs248        gr.update(visible=True),                      # build_results (reveal)249        gr.update(visible=True),                      # read_results (reveal, loading state)250        loader("READING THE PLAN · O'Brien is checking precedent and proposing settings"),251                                                      # read_loader252        vp_html,                                      # vprint253        gr.update(value=1),                           # vp_slider reset254        layer_img,                                    # vp_layer255        gr.update(interactive=True),                  # to_print_btn (un-gate)256        gr.update(visible=False),                     # override_btn (hide)257        "",                                           # second_opinion_panel (clear stale)258        gr.update(value="Engineer's Read"),           # read_toggle (reset to the read)259        gr.update(visible=True),                      # eng_read_group260        gr.update(visible=False),                     # second_op_group261    )262 263 264def load_benchy():265    mesh = benchy_mesh()266    if not mesh:267        return ({"geometry": None, "mesh": None, "label": None, "read": None},268                gr.update(value=None), "**BENCHY MISSING** — add assets/benchy.glb", gr.update())269    geo, read = infer_geometry(mesh)270    return _set_part(geo, mesh, "3DBENCHY (CC0)", read)271 272 273def generate_part(kind, size):274    mesh, geo = generate_primitive(kind, size or 30)275    return _set_part(geo, mesh, f"GENERATED {str(kind).upper()} · {float(size or 30):.0f}MM")276 277 278def upload_part(f):279    if not f:280        return (gr.update(), gr.update(), gr.update(), gr.update())281    from pathlib import Path as _P282    geo, read = infer_geometry(f.name)283    return _set_part(geo, f.name, f"UPLOAD · {_P(f.name).name[:26]}", read)284 285 286def scrub_layer(idx, part):287    """Render one cross-section layer at full fidelity for the scrubber slider."""288    mesh = (part or {}).get("mesh")289    return layer_image(mesh, idx)290 291 292# ── model warm-up + live status + switcher (Live / LoRA / QAT) ────────────────293def _status_html() -> str:294    return f"<div class='ce-sub' style='font-size:12px;'>MODEL · {llm.backend_status()}</div>"295 296 297def warm_up_pending() -> str:298    """Header icon stays; the warm-up intent is signaled by a toast."""299    gr.Info("Warming up the model (first load can take ~30s on ZeroGPU)…")300    return _model_icon()301 302 303def warm_up_cb() -> str:304    """After warm-up, keep the info icon in the header."""305    return _model_icon()306 307 308def _model_icon() -> str:309    """Small info-icon callout for the header row. No text — the dropdown + warm-up310    button already give enough context; the tooltip explains the backends."""311    return (312        "<span class='ce-callout' tabindex='0'>" + icon("info") + ""313        "<span class='ce-tip'>The <b>LoRA v2/v3</b> adapters serve via ZeroGPU on the Space; "314        "<b>Base</b> is the stock Gemma 4 E4B; <b>Modal API</b> is the remote endpoint. "315        "Local users: pull the LoRA from HF Hub "316        "(kylebrodeur/microfactory-node-lora-v2 / -v3-qat) or via ollama.</span></span>"317    )318 319 320def _model_callout() -> str:321    """Legacy full-width model callout (used when more explanatory text is wanted)."""322    return (323        "<span class='ce-callout' tabindex='0'>" + icon("info") + " MODEL INFO"324        "<span class='ce-tip'>The <b>LoRA v2/v3</b> adapters serve via ZeroGPU on the Space; "325        "<b>Base</b> is the stock Gemma 4 E4B; <b>Modal API</b> is the remote endpoint. "326        "Local users: pull the LoRA from HF Hub "327        "(kylebrodeur/microfactory-node-lora-v2 / -v3-qat) or via ollama.</span></span>"328    )329 330 331def select_model(choice: str) -> str:332    """Model switcher: apply the chosen backend, then return the info icon so the333    header stays compact."""334    choice = choice or MODEL_OPTIONS[0]335    try:336        _apply_model_choice(choice)337    except Exception:338        pass339    return _model_icon()340 341 342# ── simulated environment (this is a sim lab — conditions are generated, overridable) ──343def _sensor_readout(t, h, pos) -> str:344    """Compact value line for the envbar. The 'ENVIRONMENT (SIMULATED)' label is345    rendered separately in app.py so it matches the POSITION / MATERIAL labels."""346    return (f"{icon('thermo')} <b style='color:var(--ao-blue);'>{float(t):.0f}°C</b> · "347            f"{icon('droplet')} <b style='color:var(--ao-blue);'>{float(h):.0f}%RH</b> · "348            f"{icon('target')} <b style='color:var(--ao-blue);'>{pos}</b> · "349            f"{icon('printer')} <b style='color:var(--ao-outline);'>{PRINTER}</b>")350 351 352def status_footer(part, material, t, h, pos):353    """Live job context for the sticky footer strip."""354    p = part or {}355    label = p.get("label") or "no part"356    geo = p.get("geometry") or "—"357    return footer_bar(job=f"{label} · {material} · {geo}",358                      env=f"{float(t):.0f}°C / {float(h):.0f}%RH · {pos} · {PRINTER}")359 360 361def randomize_sensors():362    """Roll a plausible ambient + plate position + material — the lab's simulated sensor feed."""363    t = random.choice([18, 20, 22, 24, 26, 28, 30, 32])364    h = random.choice([30, 38, 45, 52, 60, 68])365    pos = random.choices(BED_POSITIONS, weights=[3, 2, 1])[0]   # center most common366    mat = random.choice(MATERIALS)367    return (gr.update(value=t), gr.update(value=h), gr.update(value=pos),368            gr.update(value=mat), _sensor_readout(t, h, pos))369 370 371def sync_readout(t, h, pos):372    return _sensor_readout(t, h, pos)373 374 375def build_job(part, material, description, temp, humidity, bed_position, model_choice):376    # NOTE: deliberately NOT @spaces.GPU. The GPU window lives on the inference377    # function only (core/llm_zerogpu._generate). Decorating the whole handler made378    # a ZeroGPU quota/error reject the ENTIRE build (slicer, retrieval, fallback) →379    # "Error" on the Space with no graceful fallback.380    if not (part and part.get("geometry")):   # guard: empty start, no part chosen381        return ("", "", "**Load a part on the BUILD tab** (quick-load Benchy, generate, or drop a mesh) "382                "before building.", "", "", "", "", gr.update(visible=False),383                gr.update(), {}, "", gr.update(), gr.update(), "", gr.update(visible=False))384 385    # Apply model choice before inference386    _apply_model_choice(model_choice or "LoRA v3 (QAT E4B)")387    geometry_type, mesh = part["geometry"], part.get("mesh")388    job = Job(geometry_type=geometry_type, material=material, description=description or "",389              bed_position=bed_position or "center", mesh_path=mesh)390    env = Environment(temp=float(temp), humidity=float(humidity))391 392    retrieved = LEDGER.retrieve(material, geometry_type, env.temp, env.humidity)393    references = reference_block(material)394    policy_note = POLICY.policy_note(material, geometry_type, env)395    rec = advise(job, env, retrieved, references, policy_note)396    spine = SPINE.check(rec.advice.settings, material)397    hint = steepest_overhang_hint(mesh) if geometry_type in ("overhang", "bridge") else None398    vp_html = virtual_printer_html(mesh, settings=spine.settings,399                                     caption=f"{material} · {geometry_type}")  # init on BUILD400 401    precedent = precedent_eval_html(retrieved, env)402    if retrieved:403        rows = "".join(404            f"<div style='font-family:ui-monospace,monospace;font-size:11px;color:var(--ao-outline);'>"405            f"• {e.job_id} ({e.source}) {e.outcome} @ {e.env_temp:.0f}°C/{e.env_humidity:.0f}% "406            f"(dist {dist:.2f})</div>"407            for e, dist in retrieved408        )409        precedent += f"<div style='margin-top:4px;'>{rows}</div>"410 411    fb = " · deterministic fallback" if rec.used_fallback else ""412    spine_md = (f"**{icon('shield')} Spine veto:** " + "  \n".join(spine.vetoes)) if spine.vetoes else ""413    confirm_vis = gr.update(visible=spine.requires_approval)414    approval_md = ("**HITL gate:** the Spine clamped a boundary setting — review, then **Confirm & Print**."415                   if spine.requires_approval else "Within safe envelope — ready when you are.")416    spine_notes_text = f"{spine_md}\n\n{approval_md}" if spine_md else approval_md417    session_id = uuid.uuid4().hex418    state = {"job": job.model_dump(), "env": env.model_dump(), "settings": spine.settings.model_dump(),419             "advice": rec.advice.model_dump(), "label": part.get("label"), "session_id": session_id,420             "spine_notes": spine_notes_text}421 422    # ── field log (Space only — gated on HF_TOKEN; local/offline no-ops) ──423    field_log.log_build(424        job=state["job"], env=state["env"], settings=state["settings"],425        advice=state["advice"], backend=rec.backend, used_fallback=rec.used_fallback,426    )427    # ── deliberation log: O'Brien proposes -> Spine vetoes (same gate) ──428    s = spine.settings429    deliberation_log.log_turns(session_id, "preflight", [430        {"agent": "O'Brien", "act": "propose",431         "content": f"{rec.advice.reasoning} Proposed: nozzle {s.nozzle_temp:.0f}°C, bed "432                    f"{s.bed_temp:.0f}°C, fan {s.fan_pct:.0f}%, retraction {s.retraction_mm:.1f}mm."},433        {"agent": "Spine", "act": "veto",434         "stance": "clamped" if spine.requires_approval else "clear",435         "content": ("Clamped: " + " · ".join(spine.vetoes)) if spine.vetoes436                    else "Within the safe envelope for this material — no clamp."},437    ], _delib_ctx(job, env))438 439    return (440        f"{rec.backend}{fb}",                                          # backend status441        precedent,                                                     # precedent442        f"**Chief Engineer O'Brien:** {rec.advice.reasoning}",         # reasoning443        risk_callouts_html(rec.advice.risks, hint) + placement_callout(material, bed_position),  # risks444        settings_panel_html(spine.settings, material),                 # settings (LCARS panel)445        spine_notes_text,                                                # spine notes446        gcode_panel_html(spine.settings, material),                    # g-code (LCARS panel)447        confirm_vis,                                                   # confirm visibility448        render_node_cards(env, working=True),                          # node cards449        state,                                                         # state450        virtual_printer_html(mesh, settings=spine.settings,451                              caption=f"{material} · {geometry_type}"),  # vprint (refined caption)452        gr.update(value=1),                                            # reset layer scrubber453        layer_image(mesh, 1),                                          # initial scrubbed layer454        "",                                                            # read_loader (clear)455    )456 457 458def _compute_second_opinion(state):459    """Run the Inspector (La Forge) critique once. Returns the raw tuple used by460    the UI: (panel_html, to_print_update, override_update)."""461    if not state or "advice" not in state:462        return ("<div class='ce-sub'>Build a job first — then I'll give the plan a second look.</div>",463                gr.update(interactive=True), gr.update(visible=False))464    job = Job(**state["job"])465    env = Environment(**state["env"])466    settings = PrintSettings(**state["settings"])467    advice = Advice(**state["advice"])468    verdict = inspector.second_opinion(job, env, settings, advice)469    field_log.log_event("second_opinion", {"material": job.material, "geometry": job.geometry_type,470                                            "inspector_stance": verdict.stance,471                                            "inspector_headline": verdict.headline})472    deliberation_log.log_turns(state.get("session_id"), "preflight", [473        {"agent": "La Forge", "act": "second_opinion", "stance": verdict.stance,474         "content": f"{verdict.headline} — {verdict.detail}"},475    ], _delib_ctx(job, env))476    panel = inspector_panel(verdict, label="LA FORGE · SECOND OPINION (PRE-PRINT)")477    if verdict.stance.lower() == "dispute":478        panel += ("<div style='margin-top:6px;padding:6px 10px;border-left:3px solid var(--ao-red,#d9534f);"479                  "background:var(--ao-surface);font-family:ui-monospace,monospace;font-size:12px;"480                  "color:var(--ao-text);'>" + icon('alert') + " <b>The Inspector disputes this plan.</b> "481                  "→ PRINT is held. Review the objection, then acknowledge to proceed anyway.</div>")482        return panel, gr.update(interactive=False), gr.update(visible=True)483    return panel, gr.update(interactive=True), gr.update(visible=False)484 485 486def second_opinion(state):487    """Idempotent wrapper: if La Forge already weighed in on this build, return the488    cached verdict; otherwise compute it once and store it in state."""489    if state and state.get("second_opinion"):490        return state["second_opinion"]491    result = _compute_second_opinion(state)492    if state:493        state["second_opinion"] = result494    return result495 496 497def toggle_read(choice, state):498    """Segmented toggle on Build: flip between Engineer's Read and Second Opinion,499    showing one panel at a time. The opinion is computed lazily on first reveal and500    then cached for the rest of the build. While it computes, the Second Opinion panel501    shows a mini-loader so the user knows La Forge is reviewing the plan."""502    if str(choice).lower().startswith("engineer"):503        yield (gr.update(visible=True), gr.update(visible=False),504               "", gr.update(), gr.update(), state)505        return506    cached = state.get("second_opinion") if state else None507    if cached:508        panel, to_print, override = cached509        yield (gr.update(visible=False), gr.update(visible=True), panel, to_print, override, state)510        return511    # show in-place loader while the Inspector runs512    yield (gr.update(visible=False), gr.update(visible=True),513           loader("CONSULTING LA FORGE", stages=[514               "reading the engineer's plan",515               "checking precedent and risks",516               "comparing settings to the safe envelope",517               "drafting the pre-print verdict",518           ]), gr.update(interactive=True), gr.update(visible=False), state)519    panel, to_print, override = _compute_second_opinion(state)520    state = (state or {}) | {"second_opinion": (panel, to_print, override)}521    yield (gr.update(visible=False), gr.update(visible=True), panel, to_print, override, state)522 523 524def ack_override(state):525    """Human overrides the Inspector's dispute — re-open → PRINT (on the operator's call)."""526    if state and state.get("session_id"):527        deliberation_log.log_turns(state["session_id"], "preflight", [528            {"agent": "Operator", "act": "override", "stance": "override",529             "content": "Acknowledged La Forge's objection. Proceeding to print on the operator's call."},530        ], _delib_ctx(Job(**state["job"]), Environment(**state["env"])))531    return gr.update(interactive=True), gr.update(visible=False)532 533 534def job_readout(state):535    """The job Print inherits from Studio/Build (no re-picking — it prints THIS job)."""536    if not state or "job" not in state:537        return ("<div class='ce-sub'>No job built yet. Go to <b>BUILD</b> → define a part → "538                "<b>SLICE</b>, then return here to print it.</div>")539    j, e = state["job"], state["env"]540    return (f"<div class='ce-sub' style='font-size:13px;'>PRINTING · "541            f"<b style='color:var(--ao-orange);'>{state.get('label') or j['geometry_type']}</b> · "542            f"{j['material']}/{j['geometry_type']} · {icon('target')} {j.get('bed_position','center')} · "543            f"{icon('thermo')} {e['temp']:.0f}°C / {icon('droplet')} {e['humidity']:.0f}%RH · "544            f"{icon('printer')} {PRINTER}</div>")545 546 547def plan_card_html(state):548    """THE PLAN card: Spine-validated proposed settings + Spine notes for the current job."""549    if not state or "settings" not in state:550        return ("<div class='ce-sub'>No plan built yet. Go to <b>BUILD</b> → define a part → "551                "<b>SLICE</b>, then return here to print it.</div>")552    settings = PrintSettings(**state["settings"])553    material = state["job"]["material"]554    html = settings_panel_html(settings, material)555    notes = state.get("spine_notes") or ""556    if notes:557        html += ("<div class='ce-sub' style='margin-top:6px;'>" +558                 notes.replace("\n", "<br>") + "</div>")559    return html560 561 562def plan_verdict_html(state):563    """La Forge's pre-print stance if already computed on the SLICE tab."""564    if not state or "settings" not in state:565        return ("<div class='ce-sub'>Build a job first to get La Forge's pre-print stance.</div>")566    cached = state.get("second_opinion")567    if cached:568        return cached[0]569    return ("<div class='ce-sub'>" + icon("search") +570            " Run the Inspector (<b>Second Opinion</b>) on the SLICE tab for a pre-print stance.</div>")571 572 573def plan_testing_html(state):574    """THE PLAN header: restate plainly what this run tests (job + conditions + question)."""575    if not state or "job" not in state:576        return ("<div class='ce-sub'>No job yet — build one on <b>BUILD → SLICE</b>, then return "577                "here to print it.</div>")578    j, e = state["job"], state["env"]579    return ("<div class='ce-sub' style='font-size:13px;'>Testing the engineer's plan for "580            f"<b style='color:var(--ao-orange);'>{state.get('label') or j['geometry_type']}</b> "581            f"({j['material']}/{j['geometry_type']}) at {e['temp']:.0f}°C / {e['humidity']:.0f}%RH on "582            f"{PRINTER}: <i>does it print clean, and does the policy improve across the run?</i></div>")583 584 585def _next_steps_html(state):586    """NEXT STEPS for the REVIEW record — what the run earned and what to do next."""587    run = state.get("run") if state else None588    j = state["job"]589    steps = []590    if run:591        if run.get("first"):592            steps.append(f"Converged to a clean print by iteration <b>{run['first']}</b> — the learned "593                         f"policy for {j['material']}/{j['geometry_type']} at these conditions is now stored "594                         "and will pre-bias the next similar job.")595        else:596            steps.append("No clean print this run — the job is genuinely hard for these conditions or the "597                         "policy is saturated. Worth a human look, an <b>OVERRIDE</b>, or logging a real outcome.")598        steps.append("Print this on the real machine and use <b>LOG A REAL PRINT</b> to feed the true "599                     "outcome back into the ledger.")600    else:601        steps.append("Run the <b>PRINT</b> loop to simulate this job and learn from the outcome.")602    return "".join(f"<div class='ce-sub' style='margin:3px 0;'>{icon('arrow')} {s}</div>" for s in steps)603 604 605def review_record_html(state):606    """The full session record on REVIEW: inputs → O'Brien's read → La Forge's607    pre-print stance → the simulated run → outcome → next steps. Assembled from608    state so the whole story of this job lives in one place."""609    if not state or "job" not in state:610        return ("<div class='ce-sub'>No session yet. Build a job (<b>BUILD → SLICE</b>), get the read "611                "and a second opinion, then <b>PRINT</b> — the full record assembles here.</div>")612    j, e = state["job"], state["env"]613    p = []614    p.append(rule("INPUTS · THE JOB"))615    p.append("<div class='ce-sub' style='font-size:13px;'>"616             f"<b style='color:var(--ao-orange);'>{state.get('label') or j['geometry_type']}</b> · "617             f"{j['material']}/{j['geometry_type']} · {icon('target')} {j.get('bed_position','center')} · "618             f"{icon('thermo')} {e['temp']:.0f}°C / {icon('droplet')} {e['humidity']:.0f}%RH · "619             f"{icon('printer')} {PRINTER}</div>")620    p.append(rule("CHIEF ENGINEER O'BRIEN · THE READ"))621    adv = state.get("advice") or {}622    p.append(f"<div class='ce-sub'>{adv.get('reasoning') or '(no read captured this session)'}</div>")623    p.append(rule("LA FORGE · PRE-PRINT SECOND OPINION"))624    cached = state.get("second_opinion")625    p.append(cached[0] if cached else626             "<div class='ce-sub'>No second opinion captured — run it on the SLICE tab (THE READ → Second Opinion).</div>")627    run = state.get("run")628    p.append(rule("SIMULATED PRINT RUN"))629    if run:630        p.append(run["curve_html"])631        p.append(run["log_html"])632        p.append(rule("OUTCOME · WHAT HAPPENED"))633        p.append(run["outcome_html"])634    else:635        p.append("<div class='ce-sub'>Not printed yet — run the PRINT loop to see the simulated run + outcome.</div>")636    p.append(rule("NEXT STEPS"))637    p.append(_next_steps_html(state))638    return "".join(p)639 640 641 642def _print_plan_values(state):643    """Return the Engineer's proposed settings values (or safe defaults) for VARY sliders."""644    defaults = {"nozzle_temp": 200, "bed_temp": 60, "fan_pct": 80, "retraction_mm": 4.5}645    if not state or "settings" not in state:646        return defaults647    s = state["settings"]648    return {k: s.get(k, defaults[k]) for k in defaults}649 650 651def apply_overrides(state, nozzle, bed, fan, retract):652    """Compare VARY slider values to the Engineer's plan. If anything changed, build an653    override PrintSettings and log that the operator defied the plan. Returns the override654    dict (or None when the plan is unchanged)."""655    if not state or "settings" not in state:656        return None657    plan = PrintSettings(**state["settings"])658    diffs = {}659    if abs(float(nozzle) - plan.nozzle_temp) > 1e-6:660        diffs["nozzle_temp"] = float(nozzle)661    if abs(float(bed) - plan.bed_temp) > 1e-6:662        diffs["bed_temp"] = float(bed)663    if abs(float(fan) - plan.fan_pct) > 1e-6:664        diffs["fan_pct"] = float(fan)665    if abs(float(retract) - plan.retraction_mm) > 1e-6:666        diffs["retraction_mm"] = float(retract)667    if not diffs:668        state.pop("print_overrides", None)669        return None670    overrides = plan.model_copy(update=diffs)671    state["print_overrides"] = overrides.model_dump()672    job = Job(**state["job"])673    env = Environment(**state["env"])674    field_log.log_print_override(job.model_dump(), env.model_dump(), overrides.model_dump())675    if state.get("session_id"):676        deliberation_log.log_turns(state["session_id"], "preflight", [677            {"agent": "Operator", "act": "override",678             "content": (f"Overrode the Engineer: nozzle {overrides.nozzle_temp:.0f}°C, "679                         f"bed {overrides.bed_temp:.0f}°C, fan {overrides.fan_pct:.0f}%, "680                         f"retraction {overrides.retraction_mm:.1f}mm.")},681        ], _delib_ctx(job, env))682    return overrides.model_dump()683 684 685def _simulated_result_panel(sess, run_summary, material, geometry_type, env, label) -> str:686    """Two-zone outcome — the dominant SIMULATED RESULT zone (the compact LOG A REAL687    PRINT zone is static UI below). Shows the final outcome, the climb, whether the688    Inspector's prediction held, and La Forge's run verdict."""689    traj = sess.trajectory690    final = sess.records[-1].result691    first = sess.first_success692    passed = final.outcome == "success"693    col = "var(--ao-green)" if passed else "var(--ao-red)"694    badge = "PASS" if passed else "FAIL"695    climb = (f"first clean print at iteration <b>{first}</b>" if first696             else f"still improving — best <b>{max(traj):.2f}</b>")697    return (698        "<div style='font-family:ui-monospace,monospace;background:var(--ao-void);"699        "border:1px solid var(--ao-outline-dim);border-left:3px solid var(--ao-orange);padding:10px 12px;'>"700        f"<div style='color:var(--ao-orange);font-weight:700;letter-spacing:2px;font-size:11px;'>"701        f"{icon('flask')} SIMULATED RESULT <span style='color:var(--ao-outline);font-weight:400;'>"702        "(deterministic world — stand-in for printer + sensors)</span></div>"703        f"<div class='ce-sub' style='margin-top:6px;'>WHAT WAS SIMULATED · {material}/{geometry_type} "704        f"· {env.temp:.0f}°C/{env.humidity:.0f}%RH · {PRINTER}</div>"705        f"<div style='margin-top:4px;font-size:15px;'>FINAL · "706        f"<span style='color:{col};font-weight:700;'>[{badge}] {final.detail}</span></div>"707        f"<div class='ce-sub'>Started at quality <b>{traj[0]:.2f}</b>; {climb}; now <b>{traj[-1]:.2f}</b> "708        f"over {len(traj)} runs.</div></div>"709        + inspector_panel(run_summary, label="LA FORGE · RUN VERDICT")710    )711 712 713def run_print(state, iterations, nozzle, bed, fan, retract, progress=gr.Progress()):714    """PRINT: run THIS job (inherited from Build) through the closed loop.715    Now a generator so the iteration log + quality chart + policy cell fill in716    live as each iteration completes. Per-iteration timing is shown next to each row."""717    if not state or "job" not in state:718        gr.Warning("Build a job first (BUILD → SLICE), then print it here.")719        yield (gr.update(),) * 9720        return721    job = Job(**state["job"])722    env = Environment(**state["env"])723    material, geometry_type = job.material, job.geometry_type724    key = cell_key(material, geometry_type, env)725    before_html = policy_cell_html(POLICY.cell_stats(material, geometry_type, env), key)726 727    overrides_dict = apply_overrides(state, nozzle, bed, fan, retract)728    overrides = PrintSettings(**overrides_dict) if overrides_dict else None729 730    records, verdicts, timings = [], [], []731    n_iters = int(iterations)732    start_iter = time.perf_counter()733    for i in range(1, n_iters + 1):734        progress(i / n_iters, desc=f"Iteration {i}/{n_iters}")735        record = run_iteration(job, env, POLICY, LEDGER, i, overrides=overrides)736        verdict = inspector.grade_iteration(geometry_type, record.result)737        elapsed = time.perf_counter() - start_iter738        start_iter = time.perf_counter()739        records.append(record)740        verdicts.append(verdict)741        timings.append(elapsed)742        yield (743            gr.update(visible=True),                                 # results_group744            "",                                                      # outcome_panel745            "",                                                      # p_headline746            quality_curve_html([r.result.quality for r in records]), # p_curve747            iteration_log_html(records, verdicts, timings),          # p_log748            policy_cell_html(POLICY.cell_stats(material, geometry_type, env), key),  # p_policy749            gr.update(),                                             # ledger_panel750            gr.update(),                                             # node_cards751            "",                                                      # review_summary752        )753 754    after = POLICY.cell_stats(material, geometry_type, env)755    run_summary = inspector.summarize_run(records, material=material, geometry=geometry_type)756 757    traj = [r.result.quality for r in records]758    first = next((r.n for r in records if r.result.outcome == "success"), None)759    field_log.log_event("print_run", {"material": material, "geometry": geometry_type,760                                       "env_temp": env.temp, "env_humidity": env.humidity,761                                       "iterations": len(records), "q_start": round(traj[0], 3),762                                       "q_end": round(traj[-1], 3), "first_clean": first,763                                       "inspector_stance": run_summary.stance,764                                       "used_override": overrides is not None,765                                       "override_settings": overrides.model_dump() if overrides else None})766    # ── deliberation log: World simulates -> La Forge grades, per iteration; then verdict ──767    ctx = _delib_ctx(job, env)768    loop_turns = []769    for r, g in zip(records, verdicts):770        clamp = " (Spine clamped a setting)" if r.clamped else ""771        loop_turns.append({"agent": "World", "act": "simulate", "stance": r.result.outcome,772                           "content": f"Iteration {r.n}: {r.result.detail}.{clamp} Policy: {r.learned}."})773        loop_turns.append({"agent": "La Forge", "act": "grade", "stance": g.stance,774                           "content": f"{g.headline} — {g.detail}"})775    deliberation_log.log_turns(state.get("session_id"), "print-loop", loop_turns, ctx)776    deliberation_log.log_turns(state.get("session_id"), "review", [777        {"agent": "La Forge", "act": "verdict", "stance": run_summary.stance,778         "content": f"{run_summary.headline} — {run_summary.detail}"},779    ], ctx)780    headline = (781        f"**{state.get('label') or geometry_type} · {material} @ {env.temp:.0f}°C / {env.humidity:.0f}% RH** — "782        f"started at quality **{traj[0]:.2f}** ({records[0].result.outcome}); "783        + (f"first clean print at **iteration {first}**, now **{traj[-1]:.2f}**."784           if first else f"still improving — best **{max(traj):.2f}** after {len(traj)} runs.")785        + " The Engineer proposed; a separate simulated world reported the outcome; the **Inspector** "786        "graded each run; the policy and ledger learned. *(Simulated — see [SIMULATION.md](docs/reference/SIMULATION.md).)*"787    )788    policy_html = (f"{before_html}<div style='text-align:center;color:var(--ao-orange);font-size:11px;"789                   f"letter-spacing:2px;'>{icon('arrow')} LEARNED</div>{policy_cell_html(after, key)}")790    outcome = _simulated_result_panel(791        SessionResult(job=job, env=env, records=records),792        run_summary, material, geometry_type, env, state.get("label"))793    # Stash the run on state so the REVIEW tab can assemble the full session record.794    state["run"] = {795        "iterations": len(records), "traj": traj, "first": first,796        "outcome": records[-1].result.outcome, "detail": records[-1].result.detail,797        "headline": headline, "curve_html": quality_curve_html(traj),798        "log_html": iteration_log_html(records, verdicts, timings),799        "outcome_html": outcome, "verdict_stance": run_summary.stance,800    }801    yield (802        gr.update(visible=True),                                  # results_group803        outcome,                                                  # outcome_panel804        headline,                                                 # p_headline805        quality_curve_html(traj),                                 # p_curve806        iteration_log_html(records, verdicts, timings),           # p_log807        policy_html,                                              # p_policy808        ledger_html(),                                            # ledger_panel809        render_node_cards(env, working=False),                    # node_cards810        inspector_panel(run_summary, label="LA FORGE · RUN VERDICT"),  # review_summary811    )812 813 814def record_outcome(outcome, state):815    """LOG A REAL PRINT: a human reports what actually happened on the real machine,816    feeding a real outcome back into the ledger (use the tool today, then teach it)."""817    if not state or "job" not in state:818        gr.Warning("Build a job first (BUILD → SLICE), then record a real outcome here.")819        return gr.update(), ledger_html(), render_node_cards(Environment(temp=22, humidity=45))820    job = Job(**state["job"])821    env = Environment(**state["env"])822    settings = PrintSettings(**state["settings"])823    entry = reflect_on_job(job, env, settings, outcome, LEDGER)824    field_log.log_event("record", {"material": job.material, "geometry": job.geometry_type,825                                    "env_temp": env.temp, "env_humidity": env.humidity, "outcome": outcome})826    msg = (f"<div class='ce-sub'>{icon('book')} Real outcome logged (earned): "827           f"<i>{entry.lesson}</i></div>")828    return msg, ledger_html(), render_node_cards(env, working=False)829 830 831def launch(**kw):832    """Single launch entrypoint so the Astrometrics theme/CSS/clock apply833    everywhere the app is started (Gradio 6 takes these on launch, not Blocks)."""834    return build().queue().launch(theme=THEME, css=CSS, head=VP_HEAD, **kw)835 836 837def _action_bar(reset_btn_label="RESET", primary_label=None, primary_variant="primary",838                primary_id=None, primary_arrow=True):839    """Build the consistent top-right action bar (small primary + persistent Reset).840    Both buttons are scale=0 so they DON'T stretch full-width. Primary buttons get841    a right-side arrow icon by default to signal "proceed to next stage".842    If primary_id is omitted, a stable kebab-case id is derived from the label."""843    with gr.Row(elem_classes=["ce-actionbar"]):844        reset = gr.Button(reset_btn_label, elem_classes=["ce-pillbtn", "ce-act"], scale=0)845        primary = None846        if primary_label:847            primary_classes = ["ce-act"]848            if primary_arrow:849                primary_classes.append("ce-icon-arrow-after")850            if primary_id is None:851                primary_id = "ce-" + primary_label.lower().replace(" ", "-").replace("(", "").replace(")", "").replace("'", "")852            primary = gr.Button(primary_label, variant=primary_variant,853                                elem_classes=primary_classes, elem_id=primary_id, scale=0)854    return reset, primary855 856 857def refresh_tabs(state):858    """Refresh on every tab visit: the PRINT plan readouts, the override sliders859    (seeded to the Engineer's plan), and the REVIEW session record."""860    vals = _print_plan_values(state)861    return (job_readout(state), plan_testing_html(state), plan_card_html(state),862            plan_verdict_html(state),863            gr.update(value=vals["nozzle_temp"]), gr.update(value=vals["bed_temp"]),864            gr.update(value=vals["fan_pct"]), gr.update(value=vals["retraction_mm"]),865            review_record_html(state))866 867 868def seed_plan_sliders(state):869    """Reset the override sliders back to the Engineer's proposed plan."""870    vals = _print_plan_values(state)871    return (gr.update(value=vals["nozzle_temp"]), gr.update(value=vals["bed_temp"]),872            gr.update(value=vals["fan_pct"]), gr.update(value=vals["retraction_mm"]))873 874 875# outputs touched by Reset (shared by the persistent Reset on every tab)876def _reset_outputs(ledger_panel, node_cards, review_summary, p_curve, p_policy, p_log,877                   p_headline, outcome_panel, results_group, real_log_msg, studio_log,878                   part, model3d, part_placeholder, part_status, build_results,879                   read_results, tabs, review_record):880    return [ledger_panel, node_cards, review_summary, p_curve, p_policy, p_log,881            p_headline, outcome_panel, results_group, real_log_msg, studio_log,882            part, model3d, part_placeholder, part_status, build_results, read_results, tabs,883            review_record]884 885 886def build() -> gr.Blocks:887    with gr.Blocks(title="Microfactory Node: 3D Printer") as demo:888        gr.HTML(command_bar(llm.backend_status()))889        # header row: dropdown + warm button + info icon only (NO background tints, NO stretching)890        with gr.Row(elem_id="ce-modelswitch"):891            model_select = gr.Dropdown(MODEL_OPTIONS, value=MODEL_OPTIONS[0],892                                       show_label=False, container=False,893                                       elem_classes=["ce-modeldd"], scale=0)894            warm_btn = gr.Button("WARM UP", elem_id="ce-warm",895                                   elem_classes=["ce-pillbtn", "ce-icon-bolt"], scale=0)896            model_status = gr.HTML(_model_icon(), elem_classes=["ce-status-inline"])897 898        state = gr.State()899        part = gr.State({"geometry": None, "mesh": None, "label": None, "read": None})900 901        with gr.Tabs() as tabs:902            # ───────────────────────── BUILD · define the job ────────────────────────903            with gr.Tab("LOAD", id="studio"):904                # Top row: ENVIRONMENT + OVERRIDE + POSITION + MATERIAL grouped on the left;905                # RANDOMIZE / RESET / SLICE grouped on the right.906                with gr.Row(elem_classes=["ce-actionbar", "ce-envbar"], equal_height=False):907                    with gr.Column(elem_classes=["ce-inline-group", "ce-envbar-left"], scale=0):908                        with gr.Row(elem_classes=["ce-inline-pills"]):909                            with gr.Column(elem_classes=["ce-inline-group"], scale=0):910                                gr.HTML("<div class='ce-inline-label'>ENVIRONMENT (SIMULATED)</div>")911                                sensors_readout = gr.HTML(elem_classes=["ce-envbar-readout"])912                                gr.HTML("<button class='ce-pillbtn ce-icon-sliders' "913                                        "data-popup-trigger='override' type='button' "914                                        "id='ce-override' "915                                        "style='background:var(--ao-surface);color:var(--ao-orange);"916                                        "border:none;padding:5px 15px;border-radius:999px;"917                                        "text-transform:uppercase;letter-spacing:.5px;font-size:12px;"918                                        "font-weight:700;cursor:pointer;display:inline-flex;"919                                        "align-items:center;gap:6px;'>OVERRIDE</button>")920                            with gr.Column(elem_classes=["ce-inline-group"], scale=0):921                                gr.HTML("<div class='ce-inline-label'>POSITION"922                                        "<span class='ce-callout' tabindex='0'>" + icon("info", size=10) +923                                        "<span class='ce-tip'>edges/corners run cooler → warp/adhesion risk</span>"924                                        "</span></div>")925                                bed_position = gr.Radio(BED_POSITIONS, value="center", show_label=False,926                                                        elem_classes=["ce-pills"])927                            with gr.Column(elem_classes=["ce-inline-group"], scale=0):928                                gr.HTML("<div class='ce-inline-label'>MATERIAL</div>")929                                material = gr.Radio(MATERIALS, value="PLA", show_label=False, elem_classes=["ce-pills"])930                    # spacer pushes the action group to the right edge931                    gr.HTML("<div style='flex:1;'></div>")932                    with gr.Column(elem_classes=["ce-inline-group", "ce-envbar-actions"], scale=0):933                        gr.HTML("<div class='ce-inline-label'>&#8203;</div>")  # invisible label for alignment934                        with gr.Row(elem_classes=["ce-inline-pills"]):935                            roll_btn = gr.Button("RANDOMIZE", elem_id="ce-randomize",936                                                 elem_classes=["ce-pillbtn", "ce-icon-shuffle"], scale=0)937                            reset_s = gr.Button("RESET", elem_id="ce-reset-load",938                                                elem_classes=["ce-pillbtn", "ce-act"], scale=0)939                            run_btn = gr.Button("SLICE", variant="primary",940                                                elem_classes=["ce-act", "ce-icon-arrow-after"], elem_id="ce-run", scale=0)941 942                # OVERRIDE ENVIRONMENT popup (hidden by default; toggled via JS)943                gr.HTML("<div class='ce-popup-backdrop' data-popup-backdrop='override'></div>")944                with gr.Group(elem_classes=["ce-popup", "ce-popup-override"]):945                    gr.HTML("<div class='ce-popup-title'>OVERRIDE ENVIRONMENT"946                            "<span class='ce-popup-close'>✕</span></div>")947                    with gr.Row():948                        temp = gr.Number(value=22, label="AMBIENT °C", elem_classes=["ce-num"], scale=1)949                        humidity = gr.Number(value=45, label="HUMIDITY %RH", elem_classes=["ce-num"], scale=1)950                    gr.HTML("<div class='ce-sub' style='margin-top:6px;'>Override the simulated "951                            "environment to test the engineer's response to specific conditions.</div>")952 953                # PART card — intro + JOB LOG copy lives INSIDE here (not at top)954                with gr.Group(elem_classes=["ce-part-card"]):955                    gr.HTML(rule("PART"))956                    part_status = gr.Markdown("", elem_classes=["ce-pad"])957                    with gr.Row(equal_height=False, elem_classes=["ce-part-row"]):958                        # LEFT: 3D viewer with intro + job-log copy below it (moved from top)959                        with gr.Column(scale=3, elem_classes=["ce-part-viewer-col"]):960                            part_placeholder = gr.HTML(_viewer_placeholder())961                            model3d = gr.Model3D(value=None, label="", height=360, visible=False,962                                                    interactive=False)963                            # intro + job log moved DOWN into the part area964                            gr.HTML(tab_intro("Load the part, set the material and the room, then "965                                              "<b>SLICE</b> to read the engineer's pre-flight check. "966                                              "You give it the part, the material, and the room; it "967                                              "infers what kind of part this is."))968                            studio_log = gr.HTML(studio_log_html())969                        # RIGHT: dropzone + mesh-source buttons + NOTES970                        with gr.Column(scale=1, elem_classes=["ce-part-actions-col"]):971                            mesh_in = gr.File(file_types=[".stl", ".glb", ".obj"],972                                              label="UPLOAD MESH", show_label=False,973                                              elem_classes=["ce-drop"])974                            benchy_btn = gr.Button("QUICK-LOAD BENCHY", elem_id="ce-benchy",975                                                   elem_classes=["ce-pillbtn", "ce-icon-anchor",976                                                                 "ce-mesh-source"], scale=0)977                            # GENERATE A PRIMITIVE: styled to match QUICK-LOAD BENCHY (ce-mesh-source)978                            with gr.Accordion("GENERATE A PRIMITIVE", open=False,979                                              elem_classes=["ce-mesh-source", "ce-accordion-pill"]):980                                gen_kind = gr.Radio(["box", "cylinder", "cone", "sphere"], value="box",981                                                    show_label=False, elem_classes=["ce-pills"])982                                gen_size = gr.Number(value=30, label="SIZE (mm)", elem_classes=["ce-num"])983                                gen_btn = gr.Button("GENERATE", elem_id="ce-generate",984                                                    elem_classes=["ce-pillbtn"], scale=0)985                            description = gr.Textbox(label="NOTES (OPTIONAL)",986                                                     placeholder="e.g. 45° bracket, 60mm tall",987                                                     elem_classes=["ce-notes-inline"])988 989            # ───────────────── SLICE · slice + analyze + pre-flight check ─────────────990            with gr.Tab("SLICE", id="build"):991                reset_b, to_print_btn = _action_bar(primary_label="PRINT (RUN ITERATIONS)")992                gr.HTML(tab_intro("The pre-flight check, <b>before it prints</b>: slice the part, read "993                                  "precedent, flag failures, and get a second opinion. Then → PRINT."))994                with gr.Group(visible=False, elem_classes=["ce-part-card"]) as build_results:995                    # slice + motion preview side by side — equal width columns996                    gr.HTML(rule("SLICE · CROSS-SECTION + MOTION PREVIEW"))997                    with gr.Row(equal_height=False, elem_classes=["ce-part-row", "ce-slice-viz"]):998                        with gr.Column(scale=1, elem_classes=["ce-slice-col"]):999                            vp_layer = gr.Image(label="", height=360, show_label=False,1000                                            interactive=False)1001                        with gr.Column(scale=1, elem_classes=["ce-slice-col", "ce-vp"]):1002                            vprint = gr.HTML()1003                    with gr.Column(elem_classes=["ce-hslider"]):1004                        vp_slider = gr.Slider(1, SCRUB_LAYERS, value=1, step=1,1005                                               show_label=False, container=False)1006                    gr.HTML("<div class='ce-sub' style='padding:0 12px;'>Scrub through real cross-sections of "1007                            "<i>this</i> part at full mesh fidelity. The preview animates the layer rise "1008                            "while the read loads below.</div>")1009 1010                    # Engineer's Read ↔ Second Opinion (one panel at a time)1011                    with gr.Group(visible=False, elem_classes=["ce-card"]) as read_results:1012                        backend = gr.Markdown()1013                        gr.HTML(rule("THE READ"))1014                        read_loader = gr.HTML()1015                        read_toggle = gr.Radio(["Engineer's Read", "Second Opinion"],1016                                               value="Engineer's Read", show_label=False,1017                                               elem_classes=["ce-seg"])1018                        with gr.Group(visible=True, elem_classes=["ce-card"]) as eng_read_group:1019                            precedent = gr.HTML(elem_id="ce-precedent")1020                            reasoning = gr.Markdown(elem_id="ce-reasoning")1021                            risks = gr.HTML()1022                            gr.HTML(rule("VALIDATION + G-CODE"))1023                            spine_notes = gr.Markdown()1024                            with gr.Row(equal_height=False):1025                                settings_html = gr.HTML()1026                                gcode_html = gr.HTML()1027                            confirm_btn = gr.Button("CONFIRM & PRINT", elem_id="ce-confirm", visible=False)1028                        with gr.Group(visible=False, elem_classes=["ce-card"]) as second_op_group:1029                            gr.HTML("<div class='ce-sub' style='padding:0 12px;'>A separate inspector — <b>La Forge</b> — reviews "1030                                    "the plan before it prints: O'Brien is an optimist, La Forge is not.</div>")1031                            second_opinion_panel = gr.HTML()1032                            override_btn = gr.Button("PRINT ANYWAY (I'VE REVIEWED THE OBJECTION)",1033                                                     visible=False, elem_classes=["ce-pillbtn"])1034 1035            # ──────────────────── PRINT · run it, iterate, grade ─────────────────────1036            with gr.Tab("PRINT", id="print"):1037                reset_p, p_run = _action_bar(primary_label="PRINT")1038                gr.HTML(tab_intro("Print <b>this job</b> (inherited from Build). The Engineer proposes → "1039                                  "the Spine vetoes → a <b>simulated world</b> prints → the <b>Inspector "1040                                  "grades</b> → policy + ledger learn. Quality compounds fail→clean."))1041 1042                # ENVBAR: what's printing (inherited from Build)1043                with gr.Row(elem_classes=["ce-actionbar", "ce-envbar"]):1044                    p_job = gr.HTML(job_readout(None), elem_classes=["ce-envbar-readout"])1045 1046                # THE PLAN — what we're testing · the engineer's settings · what they expect1047                with gr.Group(elem_classes=["ce-card"]) as plan_card:1048                    gr.HTML(rule("THE PLAN · WHAT WE'RE TESTING"))1049                    plan_testing = gr.HTML(plan_testing_html(None))1050                    gr.HTML("<div class='ce-sub' style='margin-top:8px;color:var(--ao-orange-soft);"1051                            "letter-spacing:1px;'>ENGINEER'S PROPOSED SETTINGS · Spine-validated</div>")1052                    plan_settings = gr.HTML()1053                    gr.HTML("<div class='ce-sub' style='margin-top:8px;color:var(--ao-orange-soft);"1054                            "letter-spacing:1px;'>WHAT THE ENGINEER EXPECTS · La Forge pre-print stance</div>")1055                    plan_verdict = gr.HTML()1056                    gr.HTML("<div style='margin-top:10px;display:flex;align-items:center;gap:10px;"1057                            "flex-wrap:wrap;'>"1058                            "<button class='ce-pillbtn ce-icon-sliders' data-popup-trigger='plan' "1059                            "id='ce-override-plan' type='button' style='background:var(--ao-surface);"1060                            "color:var(--ao-orange);border:none;padding:5px 15px;border-radius:999px;"1061                            "text-transform:uppercase;letter-spacing:.5px;font-size:12px;font-weight:700;"1062                            "cursor:pointer;display:inline-flex;align-items:center;gap:6px;'>"1063                            "OVERRIDE PLAN</button>"1064                            "<span class='ce-sub'>change the settings and print against your own values "1065                            "instead of the engineer's plan</span></div>")1066 1067                # ITERATIONS — compact control + plain explainer1068                with gr.Row(elem_classes=["ce-iter-row"]):1069                    gr.HTML("<div class='ce-inline-label' style='align-self:center;"1070                            "margin:0 6px 0 2px;'>ITERATIONS</div>")1071                    p_iters = gr.Slider(1, 16, value=8, step=1, show_label=False, container=False,1072                                        elem_classes=["ce-iter-slider"])1073                    p_iter_readout = gr.HTML(1074                        "<div class='ce-sub' style='align-self:center;white-space:nowrap;'>"1075                        "<b>8</b> runs</div>")1076                gr.HTML("<div class='ce-sub' style='margin:2px 0 10px;'>How many times to print this job "1077                        "in the simulated world — each run feeds the next. 1 = single print · "1078                        "4 = quick convergence · 8 = balanced climb · 16 = full convergence run.</div>")1079 1080                # OVERRIDE PLAN popup — same component as OVERRIDE ENVIRONMENT on the BUILD tab1081                gr.HTML("<div class='ce-popup-backdrop' data-popup-backdrop='plan'></div>")1082                with gr.Group(elem_classes=["ce-popup", "ce-popup-plan"]):1083                    gr.HTML("<div class='ce-popup-title'>OVERRIDE THE ENGINEER'S PLAN"1084                            "<span class='ce-popup-close'>✕</span></div>")1085                    gr.HTML("<div class='ce-sub' style='border-left:3px solid var(--ao-red);"1086                            "padding:6px 10px;margin-bottom:8px;'>" + icon("alert") + " The run will use "1087                            "these settings instead of the Spine-validated proposal.</div>")1088                    with gr.Row():1089                        p_nozzle = gr.Slider(150, 300, value=200, step=1,1090                                             label="NOZZLE °C", elem_classes=["ce-num"])1091                        p_bed = gr.Slider(40, 120, value=60, step=1,1092                                          label="BED °C", elem_classes=["ce-num"])1093                    with gr.Row():1094                        p_fan = gr.Slider(0, 100, value=80, step=1,1095                                          label="FAN %", elem_classes=["ce-num"])1096                        p_retract = gr.Slider(0, 10, value=4.5, step=0.1,1097                                              label="RETRACTION mm", elem_classes=["ce-num"])1098                    reset_plan_btn = gr.Button("RESET TO ENGINEER'S PLAN", elem_classes=["ce-pillbtn"])1099 1100                with gr.Group(visible=False) as results_group:1101                    gr.HTML(rule("OUTCOME · WHAT HAPPENED"))1102                    outcome_panel = gr.HTML()1103                    p_headline = gr.Markdown()1104                    gr.HTML(rule("QUALITY PER ITERATION"))1105                    p_curve = gr.HTML()1106                    gr.HTML(rule("ITERATION LOG"))1107                    p_log = gr.HTML()1108                    gr.HTML(rule("LEARNED POLICY CELL"))1109                    p_policy = gr.HTML()1110                    # compact secondary zone (~20%): log a REAL print back into the ledger1111                    with gr.Row(elem_classes=["ce-card"]):1112                        with gr.Column(scale=1):1113                            gr.HTML("<div class='ce-sub'>LOG A REAL PRINT · printed this on your machine? "1114                                    "Record what actually happened — it feeds the ledger.</div>")1115                            with gr.Row(elem_id="ce-outcomes"):1116                                b_clean = gr.Button("PRINTED CLEAN")1117                                b_sag = gr.Button("SAGGED")1118                                b_string = gr.Button("STRINGING")1119                            real_log_msg = gr.HTML()1120 1121            # ───────────────── REVIEW · compounding + agent verdicts ─────────────────1122            with gr.Tab("REVIEW", id="review"):1123                reset_r, refresh = _action_bar(reset_btn_label="RESET TO BASELINE",1124                                               primary_label="REFRESH LEDGER",1125                                               primary_variant="secondary")1126                gr.HTML(tab_intro("The whole job, end to end — inputs, O'Brien's read, La Forge's "1127                                  "second opinion, the simulated run + outcome, and what the node learned. "1128                                  "Plus the live ledger and the capability mesh."))1129                gr.HTML(rule("LA FORGE · RUN VERDICT"))1130                review_summary = gr.HTML("<div class='ce-sub'>Run the Print loop to get the Inspector's "1131                                         "verdict on the whole run.</div>")1132                # Full session record — the complete story of this job in one place.1133                with gr.Group(elem_classes=["ce-card"]):1134                    gr.HTML(rule("SESSION RECORD"))1135                    review_record = gr.HTML(review_record_html(None))1136                with gr.Row():1137                    with gr.Column(elem_classes=["ce-card"]):1138                        gr.HTML(rule("LESSON LEDGER"))1139                        ledger_panel = gr.HTML(ledger_html())1140                    with gr.Column(elem_classes=["ce-card"]):1141                        with gr.Accordion("CAPABILITY MESH · NODE NETWORK (outlook view)", open=True):1142                            node_cards = gr.HTML(render_node_cards(Environment(temp=22, humidity=45)))1143 1144        footer = gr.HTML(footer_bar())1145        privacy_line = gr.HTML(visible=field_log.is_active())1146 1147        # Email signup for Microfactory updates (opt-in, privacy-first)1148        with gr.Group(elem_classes=["ce-card"]):1149            gr.HTML(rule("STAY IN THE LOOP"))1150            gr.HTML("<div class='ce-sub'>Get Microfactory updates: new nodes, build notes, and when this thing learns to stream g-code straight to the printer. One email. No spam. Unsub any time.</div>")1151            with gr.Row():1152                signup_email = gr.Textbox(placeholder="you@example.com", label="EMAIL", show_label=False,1153                                          elem_classes=["ce-notes-inline"], scale=3)1154                signup_consent = gr.Checkbox(label="Yes, email me Microfactory updates", scale=2)1155            signup_btn = gr.Button("SUBSCRIBE", elem_classes=["ce-pillbtn"])1156            signup_status = gr.HTML()1157            signup_privacy = gr.HTML(signups.privacy_notice())1158 1159        # ── wiring ──1160        reset_outs = _reset_outputs(ledger_panel, node_cards, review_summary, p_curve, p_policy,1161                                    p_log, p_headline, outcome_panel, results_group, real_log_msg,1162                                    studio_log, part, model3d, part_placeholder, part_status,1163                                    build_results, read_results, tabs, review_record)1164        preview_outs = [part, model3d, part_status, part_placeholder]1165        foot_in = [part, material, temp, humidity, bed_position]1166        benchy_btn.click(load_benchy, None, preview_outs).then(status_footer, foot_in, [footer])1167        gen_btn.click(generate_part, [gen_kind, gen_size], preview_outs).then(status_footer, foot_in, [footer])1168        mesh_in.upload(upload_part, [mesh_in], preview_outs).then(status_footer, foot_in, [footer])1169        material.change(status_footer, foot_in, [footer])1170 1171        # Model warm-up + switcher1172        warm_btn.click(warm_up_pending, None, [model_status]).then(warm_up_cb, None, [model_status])1173        model_select.change(select_model, [model_select], [model_status])1174 1175        # Simulated environment: roll on load, re-roll on demand, keep readout + footer in sync.1176        sensor_outs = [temp, humidity, bed_position, material, sensors_readout]1177        demo.load(randomize_sensors, None, sensor_outs).then(status_footer, foot_in, [footer])1178        # CLOCK_JS wires the LCARS clock, popup toggles, and loader stage cycling.1179        demo.load(None, None, None, js=CLOCK_JS)1180        demo.load(lambda: field_log.privacy_notice() if field_log.is_active() else "",1181                  None, [privacy_line])1182        signup_btn.click(signups.record_signup,1183                         [signup_email, signup_consent, gr.State("space" if signups.is_active() else "local")],1184                         [signup_status])1185        roll_btn.click(randomize_sensors, None, sensor_outs).then(status_footer, foot_in, [footer])1186        for c in (temp, humidity, bed_position, material):1187            c.change(sync_readout, [temp, humidity, bed_position], [sensors_readout]).then(1188                status_footer, foot_in, [footer])1189 1190        # Two-step BUILD: instant loader + tab-switch, then the heavy model call (reveals1191        # the results), then refresh the inherited-job readout on the Print tab.1192        build_start_outs = [tabs, build_results, read_results, read_loader, vprint, vp_slider,1193                            vp_layer, to_print_btn, override_btn, second_opinion_panel,1194                            read_toggle, eng_read_group, second_op_group]1195        build_outs = [backend, precedent, reasoning, risks, settings_html, spine_notes,1196                      gcode_html, confirm_btn, node_cards, state, vprint, vp_slider, vp_layer,1197                      read_loader]1198        # Readouts on the PRINT plan + REVIEW record + override sliders, populated on1199        # every path that lands a job (build completion, SLICE→PRINT, and tab visits).1200        plan_outs = [p_job, plan_testing, plan_settings, plan_verdict,

Showing the first 1,200 of 1254 lines. Download the file for the rest.