Spaces:
Running on Zero
Running on Zero
File size: 24,267 Bytes
77b3711 7bb2fda f9a8763 77b3711 7bb2fda 77b3711 065c4f9 77b3711 92d401f f9a8763 77b3711 bbe0bad af80e6b f9a8763 065c4f9 77b3711 7bb2fda 77b3711 7bb2fda 77b3711 bbe0bad 77b3711 f9a8763 6287002 77b3711 7bb2fda 77b3711 447d1d2 77b3711 f9a8763 6287002 77b3711 f9a8763 77b3711 6287002 f9a8763 77b3711 f9a8763 77b3711 7bb2fda 447d1d2 77b3711 447d1d2 5b66adc 7bb2fda 77b3711 447d1d2 f9a8763 77b3711 f9a8763 77b3711 bbe0bad 447d1d2 f9a8763 77b3711 5b66adc 6287002 5b66adc 6287002 5b66adc 6287002 5b66adc af80e6b 77b3711 7bb2fda 77b3711 af80e6b 77b3711 f9a8763 af80e6b 77b3711 7bb2fda f9a8763 77b3711 bbe0bad 77b3711 af80e6b 77b3711 7bb2fda 77b3711 af80e6b 77b3711 f9a8763 77b3711 92d401f 2fe7d5d e1d8e60 92d401f e1d8e60 92d401f 2fe7d5d 92d401f e1d8e60 2fe7d5d e1d8e60 2fe7d5d e1d8e60 2fe7d5d e1d8e60 92d401f e1d8e60 363fb28 2fe7d5d 92d401f 77b3711 7bb2fda 77b3711 7bb2fda 77b3711 bbe0bad 77b3711 f9a8763 7bb2fda 77b3711 d7aa77a 7bb2fda d7aa77a 7bb2fda f9a8763 d7aa77a 77b3711 d7aa77a 77b3711 447d1d2 bbe0bad 6287002 77b3711 7bb2fda f9a8763 7bb2fda 77b3711 5b66adc 77b3711 af80e6b 77b3711 7bb2fda f9a8763 7bb2fda 77b3711 92d401f e1d8e60 92d401f 2fe7d5d 92d401f 77b3711 6287002 f9a8763 6287002 5b66adc af80e6b f9a8763 92d401f 2fe7d5d 77b3711 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 | """VisualLearningIdea: translate English into 3D, then see how far 3D can simplify.
One Space for people (this UI) and for models (every public event below is an
MCP tool: demo.launch(mcp_server=True)). Helper events are api_visibility="private".
The Space only visualises; discussion happens in the chat that calls it. Every
picture comes back with a mini HTML page, because HTML is the final destination.
The page is a URL by default: geometry is for the handoff, not the loop.
"""
import html as html_lib
import json
import os
import re
import tempfile
import threading
import spaces # ZeroGPU: import before any CUDA-touching library
import gradio as gr
from vli import assets, papercraft, pipeline, scene, simplify
from vli import html as html_pages # `html` is the tools' output-mode argument
from vli.grammar import check_words as _check_words
from vli.lexicon import LEXICON, PRIMITIVE_SHAPES
threading.Thread(target=LEXICON.ensure, daemon=True).start() # warm the dictionary
def _fail(e):
raise gr.Error(str(e))
def _json(text, name, default=None):
"""Parse a JSON argument; blank -> default, junk -> a readable error."""
if not (text or "").strip():
if default is None:
raise ValueError(f"{name} is empty; translate a sentence first")
return default
try:
return json.loads(text)
except json.JSONDecodeError:
raise ValueError(f"{name} is not valid JSON")
def _seed(value):
"""Seeds arrive as strings (some MCP clients can't send numbers)."""
try:
return int(float(str(value).strip() or 0))
except ValueError:
raise ValueError(f"seed must be a whole number, got {value!r}")
def _count(value, name, lo, hi):
"""A whole number in [lo, hi], sent as a string like seeds."""
try:
n = int(float(str(value).strip()))
except ValueError:
raise ValueError(f"{name} must be a whole number, got {value!r}")
if not lo <= n <= hi:
raise ValueError(f"{name} must be between {lo} and {hi}, got {n}")
return n
HTML_MODES = ["url", "inline", "skip"]
def _html_out(page, mode):
"""The mini HTML as (file, text): a file for "url", file and text for "inline", neither for "skip".
Inline geometry costs thousands of tokens, so a model iterating on the ledger
gets the file URL and asks for the text only when it builds on the page.
"""
mode = (mode or "url").strip().lower()
if mode not in HTML_MODES:
raise ValueError(f"html must be one of {HTML_MODES}, got {mode!r}")
if mode == "skip" or not page:
return None, ""
path = os.path.join(tempfile.mkdtemp(prefix="vli_html_"), "scene.html")
with open(path, "w", encoding="utf-8") as f:
f.write(page)
return path, page if mode == "inline" else ""
@spaces.GPU(duration=60)
def generate_mesh(prompt):
"""GPU entry point for the generation tier (last in the fallback chain).
ZeroGPU needs a @spaces.GPU function at startup. Nothing calls this until
text-to-3D is wired in, so no quota is used.
"""
raise NotImplementedError("generation tier is not wired up yet")
# ---------------- MCP tools ----------------
def check_words(sentence: str, primitives_json: str = "{}") -> str:
"""Check which words of a sentence exist in the 3D vocabulary, and complete the word being typed.
A word is available only when a ready asset (SimReady or Objaverse) exists for it;
"maps_to" names the asset it would get. Words with no asset are listed in "missing":
rephrase them, or map each one to a primitive shape in primitives_json. Verbs (sat,
stood, lay, shone, faced) and prepositions (on, under, beside, near, behind, in front
of, above) are relations between meshes; whatever "shone" becomes the light source.
Args:
sentence: The English sentence, complete or partly typed.
primitives_json: JSON object mapping missing words to shapes, e.g. {"sun": "sphere"}.
Shapes: box, sphere, cylinder, cone, plane.
Returns:
JSON string: {"ok", "words": [{text, kind, available_in, maps_to | primitive}], "missing",
"primitive_shapes", "completions"}.
"""
try:
return json.dumps(_check_words(sentence, _json(primitives_json, "primitives_json", {})))
except Exception as e:
_fail(e)
def translate_to_scene(sentence: str, primitives_json: str = "{}", seed: str = "0",
html: str = "url", pronouns: str = "recent") -> tuple[str, str, str, str, str]:
"""Translate an English sentence into a 3D scene.
Produces the serial ledger (tokens in reading order, the items each word commits,
and the unresolved questions), one configuration of that ledger chosen by `seed`,
a rendered picture, a GLB model and a mini HTML page. The HTML is a self-contained
Three.js scene with inline low-poly geometry: read it, edit it, build on it. Words
with no asset must be mapped to a primitive in primitives_json (see check_words).
Args:
sentence: A sentence using available words, e.g. "the cat sat and the lamp glowed".
primitives_json: JSON object mapping missing words to shapes, e.g. {"sun": "sphere"}.
seed: Picks one of the configurations that satisfy the ledger (positions, facing, sun).
html: "url" (default) returns the mini HTML as a file URL only; "inline" also returns
its text (thousands of tokens: ask for it when building on the page); "skip" omits it.
Iterate on the ledger and picture; the ledger can always re-create the page.
pronouns: Default referent for it/he/she/they: "recent" (default) = the most recent
object; "subject" = the subject of the most recent clause. Either way the choice is
an implied question with the other objects as alternatives. Kept in the ledger.
Returns:
(ledger JSON string, rendered PNG, GLB scene file, mini HTML file, mini HTML text or "").
"""
try:
*rest, page = pipeline.translate(sentence, _json(primitives_json, "primitives_json", {}), _seed(seed),
(pronouns or "recent").strip().lower())
return (*rest, *_html_out(page, html))
except Exception as e:
_fail(e)
def refine_scene(ledger_json: str, changes_json: str = "[]", seed: str = "0",
html: str = "url") -> tuple[str, str, str, str, str]:
"""Refine a ledger: freeze some decisions and change the others, then re-render.
Changes are a JSON list of:
{"id": "n1", "freeze": true} lock an item (false unlocks)
{"id": "n1", "use": 2} swap in alternatives[2]; the old choice takes slot 2,
so using 2 again swaps back (other slots don't move)
{"id": "n1", "reject": true} reject the tier: simready -> objaverse -> primitive
(generation is not wired up: a primitive can't be rejected)
{"id": "n1", "height_m": 0.4} set the object's height in metres
{"id": "n1", "shape": "sphere"} use a primitive (box, sphere, cylinder, cone, plane)
{"reroll": true} every unfrozen object takes its next alternative;
repeated rerolls visit the whole pool, whatever the seed
{"id": "n12", "bind": "n1"} n12 is the same thing as n1: merged into it, its relations move
{"id": "q11", "bind": "n5"} the word behind reference question q11 ("it", "the cat") means n5
Frozen items cannot be changed. The configuration (placement, sun angle) is always
re-chosen from `seed`: change the seed for another valid configuration.
Args:
ledger_json: Ledger JSON from translate_to_scene or a previous refine_scene.
changes_json: JSON list of changes, as above.
seed: Picks the configuration (placement, sun). Placement scales with each
asset's size, so swapping an asset shifts offsets even at the same seed.
html: "url" (default), "inline" or "skip", as in translate_to_scene.
Returns:
(ledger JSON string, rendered PNG, GLB scene file, mini HTML file, mini HTML text or "").
"""
try:
ledger = _json(ledger_json, "ledger_json")
pipeline.apply_changes(ledger, _json(changes_json, "changes_json", []))
*rest, page = pipeline.outputs(ledger, _seed(seed))
return (*rest, *_html_out(page, html))
except Exception as e:
_fail(e)
def extend_scene(ledger_json: str, sentence: str, primitives_json: str = "{}", seed: str = "0",
html: str = "url", pronouns: str = "") -> tuple[str, str, str, str, str]:
"""Add the next sentence of a paragraph to an existing scene.
Paragraphs are sentences plus bindings. "the cat" when a cat is already in the ledger,
and pronouns (it, he, she, they), refer back to an existing item instead of adding one;
each such reference is an implied question ("which cat? the one already mentioned",
"'it' = the cat?") whose `binds` names the item. Rebind with refine_scene
{"id": "<question id>", "bind": "<item id>"}. "a/an/another cat" always adds a new one.
Args:
ledger_json: Ledger JSON from translate_to_scene, refine_scene or a previous extend_scene.
sentence: The next sentence, e.g. "the apple lay beside it".
primitives_json: JSON object mapping missing words to shapes, as in translate_to_scene.
seed: Picks one of the configurations that satisfy the whole ledger.
html: "url" (default), "inline" or "skip", as in translate_to_scene.
pronouns: "recent" or "subject", as in translate_to_scene; blank = the ledger's rule.
Returns:
(ledger JSON string, rendered PNG, GLB scene file, mini HTML file, mini HTML text or "").
"""
try:
ledger = _json(ledger_json, "ledger_json")
*rest, page = pipeline.extend(ledger, sentence, _json(primitives_json, "primitives_json", {}), _seed(seed),
(pronouns or "").strip().lower() or None)
return (*rest, *_html_out(page, html))
except Exception as e:
_fail(e)
def simplify_item(ledger_json: str, item_id: str, html: str = "url", rings: str = "8",
samples: str = "16") -> tuple[str, list, list, str, str]:
"""Build the simplification ladder for one object: full asset -> fewer faces -> rings -> convex hull -> box.
Look at where the object stops reading as its word: that rung is the least geometry
that still carries the meaning, and the low rungs are the ones that can be made
physically (fewer faces = fewer papercraft pieces).
The rings rung is a second path: `rings` horizontal sections, each outline sampled at
`samples` angles, joined into bands (one paper strip per band). The report lists every
ring's height, centre and radii, so each vertex can be checked by hand.
Args:
ledger_json: Ledger JSON from translate_to_scene or refine_scene.
item_id: The object item to simplify, e.g. "n1".
html: "url" (default), "inline" or "skip", as in translate_to_scene. The ladder page
holds several rungs of up to 1000 faces each, so inline is especially large.
rings: Number of horizontal sections for the rings rung (2-64).
samples: Points sampled around each section (3-128).
Returns:
(report JSON string, gallery of rung pictures, GLB file per rung,
mini HTML file with the low rungs side by side, mini HTML text or "").
"""
try:
ledger = _json(ledger_json, "ledger_json")
item = next((i for i in ledger["items"] if i["id"] == item_id), None)
if item is None or item["kind"] != "object":
raise ValueError(f"{item_id!r} is not an object item in this ledger")
mesh = assets.settle(item)
work = tempfile.mkdtemp(prefix="vli_ladder_")
rungs = simplify.ladder(mesh, _count(rings, "rings", 2, 64), _count(samples, "samples", 3, 128))
report, gallery, files = {"item": item_id, "word": item["word"], "rungs": []}, [], []
for k, (label, rung) in enumerate(rungs):
png = scene.render({item_id: rung}, os.path.join(work, f"{k}.png"), title=label)
slug = re.sub(r"\W+", "_", label)
glb = os.path.join(work, f"{k}_{slug}.glb")
rung.export(glb)
entry = {"rung": label, **simplify.stats(rung)}
if "rings" in rung.metadata:
entry["rings"] = rung.metadata["rings"]
report["rungs"].append(entry)
gallery.append((png, label))
files.append(glb)
return (json.dumps(report, indent=1), gallery, files,
*_html_out(html_pages.ladder_page(item, rungs), html))
except Exception as e:
_fail(e)
def papercraft_rings(ledger_json: str, item_id: str, rings: str = "8", samples: str = "16",
height_cm: str = "") -> tuple[str, list, list]:
"""Make a printable papercraft net of one object from its rings rung, on A4 pages (see simplify_item).
Every band between two rings unrolls exactly into one paper strip; bands that would
curl past ~120 degrees on paper are split into parts (2a, 2b, ...). Glue tabs sit on
one side of every joint and name the piece they glue to. Caps under 4 mm are left out
(the band closes to a point there). Cut solid lines, fold dashed lines.
Args:
ledger_json: Ledger JSON from translate_to_scene or refine_scene.
item_id: The object item to make, e.g. "n1".
rings: Number of horizontal sections (2-64). Fewer rings = fewer, simpler pieces.
samples: Points around each section (3-128). Fewer samples = fewer folds.
height_cm: Height of the paper model in cm; blank = the largest model whose biggest
piece still fits one A4 page. The report gives that A4 limit either way.
Returns:
(report JSON string with the pieces, pages and what glues to what, one PNG picture per A4
page at 150 dpi to look at, one SVG file per A4 page in mm to print).
"""
try:
ledger = _json(ledger_json, "ledger_json")
item = next((i for i in ledger["items"] if i["id"] == item_id), None)
if item is None or item["kind"] != "object":
raise ValueError(f"{item_id!r} is not an object item in this ledger")
count, n = _count(rings, "rings", 2, 64), _count(samples, "samples", 3, 128)
h = None
if str(height_cm).strip():
try:
h = float(str(height_cm).strip())
except ValueError:
raise ValueError(f"height_cm must be a number, got {height_cm!r}")
if not 1 <= h <= 200:
raise ValueError(f"height_cm must be between 1 and 200, got {h}")
_, profile = simplify.rings(assets.settle(item), count, n)
if len(profile) < 2:
raise ValueError(f"{item_id!r} has fewer than 2 sections; try more rings")
real_cm = (profile[-1]["y"] - profile[0]["y"]) * 100
a4_cm = papercraft.a4_height_cm(profile, n)
pieces, scale = papercraft.net(profile, n, h or a4_cm)
work, slug = tempfile.mkdtemp(prefix="vli_paper_"), re.sub(r"\W+", "_", item["word"])
sheets = papercraft.pages(pieces)
files, pictures, page_report = [], [], []
for k, placed in enumerate(sheets, 1):
title = f"{item['word']}: {len(profile)} rings x {n}, page {k}/{len(sheets)}"
text, size = papercraft.svg(placed, title)
path = os.path.join(work, f"{slug}_net_p{k}.svg")
with open(path, "w", encoding="utf-8") as f:
f.write(text)
files.append(path)
pictures.append((papercraft.png(placed, title, os.path.join(work, f"{slug}_net_p{k}.png")), f"page {k}"))
page_report.append({"page": k, "size_mm": size, "pieces": [pc["name"] for pc, _ in placed]})
report = {"item": item_id, "word": item["word"], "rings": len(profile), "samples": n,
"model_height_cm": round((profile[-1]["y"] - profile[0]["y"]) * scale / 10, 1),
"real_height_cm": round(real_cm, 1), "max_height_cm_for_a4": round(a4_cm, 1),
"fits_a4": bool(h is None or h <= a4_cm), "pages": page_report,
"pieces": [{"name": name, **j} for name, j in papercraft.joins(pieces).items()]}
return json.dumps(report, indent=1), pictures, files
except Exception as e:
_fail(e)
# ---------------- UI-only helpers ----------------
def _suggestions(words_json):
data = json.loads(words_json or "{}")
marks = []
for w in data.get("words", []):
if w["kind"] == "unknown":
marks.append(f"~~{w['text']}~~")
elif w["kind"] == "noun":
marks.append(f"**{w['text']}** → {w.get('maps_to', '?')}")
elif w["kind"] == "primitive":
marks.append(f"_{w['text']}_ ({w['primitive']})")
else:
marks.append(w["text"])
status = " · ".join(marks) or "_start typing_"
if data.get("missing"):
status += " \n_Struck-through words have no asset: pick a suggestion, rephrase, or choose a primitive._"
missing = data.get("missing", [])
return (status, gr.update(choices=data.get("completions", []), value=None),
gr.update(choices=missing, value=missing[0] if missing else None))
def _complete(sentence, choice):
if not choice:
return sentence
return re.sub(r"[A-Za-z]*$", "", sentence) + choice + " "
def _use_primitive(primitives_json, word, shape):
prims = _json(primitives_json, "primitives_json", {})
if word:
prims[word] = shape
return json.dumps(prims)
def _html_preview(path):
"""Live preview (sandboxed iframe) of the mini HTML file."""
if not path:
return ""
with open(path, encoding="utf-8") as f:
page = f.read()
return (f'<iframe sandbox="allow-scripts" srcdoc="{html_lib.escape(page, quote=True)}" '
'style="width:100%;height:460px;border:0;border-radius:8px"></iframe>')
EXAMPLE_CHANGES = '[\n {"id": "n1", "freeze": true},\n {"reroll": true}\n]'
DATASETS_MD = """
**Dictionary (public datasets, read-only, no account needed):**
1. [NVIDIA SimReady](https://github.com/NVIDIA-Omniverse/simready-central): metric OpenUSD assets,
e.g. [nvidia/simready-assets](https://huggingface.co/datasets/nvidia/simready-assets). License per package
(e.g. CC-BY-4.0). Only geometry and the thumbnail are downloaded, not the ~1 GB of textures.
2. [allenai/objaverse](https://huggingface.co/datasets/allenai/objaverse): the LVIS-labelled subset.
License per object; non-commercial (NC) and no-derivatives (ND) objects are skipped.
3. Missing words: a primitive you choose (box, sphere, cylinder, cone, plane). Generation comes later.
This Space only visualises and stores no token. Discuss the results in your chat
(e.g. HuggingChat via MCP): every picture comes with a mini HTML page to build on,
as a URL by default (`html="inline"` returns its text).
"""
with gr.Blocks(title="VisualLearningIdea") as demo:
gr.Markdown("# English → 3D\nType with the words the 3D dictionary has. **Bold** = an object asset; "
"verbs and prepositions are relations between meshes.")
gr.Markdown(DATASETS_MD)
with gr.Row():
sentence = gr.Textbox(label="Sentence", placeholder="the cat sat and the lamp glowed", scale=4)
seed = gr.Textbox(label="Configuration seed", value="0", scale=1)
pronoun_rule = gr.Radio(label="'it' means", choices=["recent", "subject"], value="recent", scale=1,
info="recent: last object · subject: last clause's subject")
status = gr.Markdown()
suggest = gr.Radio(label="Suggestions", choices=[], interactive=True)
with gr.Row():
missing = gr.Dropdown(label="Missing word", choices=[], interactive=True)
shape = gr.Dropdown(label="Primitive", choices=PRIMITIVE_SHAPES, value="box", interactive=True)
use_prim = gr.Button("Use primitive")
primitives = gr.Code(label="Primitives", language="json", value="{}", interactive=True)
words = gr.Code(label="Word check", language="json", visible=False)
go = gr.Button("Translate", variant="primary")
with gr.Row():
picture = gr.Image(label="Picture", type="filepath")
model = gr.Model3D(label="3D")
with gr.Accordion("Mini HTML", open=False):
html_mode = gr.Radio(label="HTML text in the result", choices=HTML_MODES, value="url",
info="url: file only · inline: also the text · skip: no page")
scene_preview = gr.HTML()
scene_file = gr.File(label="Download .html")
scene_html = gr.Code(label="HTML", language="html")
with gr.Accordion("Ledger: freeze and change (serial side)", open=False):
ledger = gr.Code(label="Ledger", language="json", interactive=True)
changes = gr.Code(label="Changes", language="json", value=EXAMPLE_CHANGES, interactive=True)
refine = gr.Button("Refine")
with gr.Row():
next_sentence = gr.Textbox(label="Next sentence (refers back: 'the cat', 'it')", scale=4)
extend = gr.Button("Add sentence", scale=1)
with gr.Accordion("Simplification ladder", open=False):
with gr.Row():
item_id = gr.Textbox(label="Object item id", value="n1")
ring_count = gr.Textbox(label="Rings", value="8")
ring_samples = gr.Textbox(label="Samples per ring", value="16")
simp = gr.Button("Simplify")
ladder_pics = gr.Gallery(label="Rungs", columns=4)
ladder_preview = gr.HTML()
ladder_file = gr.File(label="Download .html")
ladder_html = gr.Code(label="HTML", language="html")
ladder_report = gr.Code(label="Report", language="json")
ladder_files = gr.File(label="GLB per rung", file_count="multiple")
with gr.Accordion("Papercraft net (rings)", open=False):
paper_height = gr.Textbox(label="Model height in cm (blank = largest that fits A4)", value="")
paper = gr.Button("Make net")
paper_pages = gr.Gallery(label="A4 pages", columns=3)
paper_file = gr.File(label="A4 pages to print (.svg, mm)", file_count="multiple")
paper_report = gr.Code(label="Pieces", language="json")
gr.on([sentence.change, primitives.change], check_words, [sentence, primitives], words,
api_name="check_words", trigger_mode="always_last") \
.then(_suggestions, words, [status, suggest, missing], api_visibility="private")
suggest.select(_complete, [sentence, suggest], sentence, api_visibility="private")
use_prim.click(_use_primitive, [primitives, missing, shape], primitives, api_visibility="private")
go.click(translate_to_scene, [sentence, primitives, seed, html_mode, pronoun_rule],
[ledger, picture, model, scene_file, scene_html], api_name="translate_to_scene") \
.then(_html_preview, scene_file, scene_preview, api_visibility="private")
refine.click(refine_scene, [ledger, changes, seed, html_mode],
[ledger, picture, model, scene_file, scene_html], api_name="refine_scene") \
.then(_html_preview, scene_file, scene_preview, api_visibility="private")
extend.click(extend_scene, [ledger, next_sentence, primitives, seed, html_mode, pronoun_rule],
[ledger, picture, model, scene_file, scene_html], api_name="extend_scene") \
.then(_html_preview, scene_file, scene_preview, api_visibility="private")
simp.click(simplify_item, [ledger, item_id, html_mode, ring_count, ring_samples],
[ladder_report, ladder_pics, ladder_files, ladder_file, ladder_html], api_name="simplify_item") \
.then(_html_preview, ladder_file, ladder_preview, api_visibility="private")
paper.click(papercraft_rings, [ledger, item_id, ring_count, ring_samples, paper_height],
[paper_report, paper_pages, paper_file], api_name="papercraft_rings")
if __name__ == "__main__":
demo.launch(mcp_server=True)
|