File size: 2,740 Bytes
df84e45 | 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 | """Expert trajectories: the ground truth replayed as the actions that build it.
Every Crello template decomposes into one `add_*` call per layer, back to
front, with exact arguments. Replaying that sequence reconstructs the ground
truth, and scores 1.0, which makes it both the SFT corpus and the strongest
test the environment has: if the expert does not score 1.0, the reward or the
tools are wrong.
The same trajectory is emitted for the HTML surface as a single page.
"""
from __future__ import annotations
from .doc import DesignDoc, Element
from .html_profile import doc_to_html
def _round(v: float) -> float:
r = round(float(v), 2)
return int(r) if r == int(r) else r
def element_call(el: Element) -> tuple[str, dict]:
box = {"x": _round(el.x), "y": _round(el.y)}
extra = {}
if el.angle:
extra["angle"] = _round(el.angle)
if el.opacity < 1:
extra["opacity"] = _round(el.opacity)
if el.kind == "image":
return "add_image", {
"asset_id": el.asset_id, **box, "width": _round(el.width),
"height": _round(el.height), **extra,
}
if el.kind == "shape":
args = {"shape": el.shape, **box, "width": _round(el.width), "height": _round(el.height),
"fill": el.fill or "none", **extra}
if el.stroke and el.stroke_width:
args.update(stroke=el.stroke, stroke_width=_round(el.stroke_width))
if el.radius:
args["radius"] = _round(el.radius)
return "add_shape", args
args = {"text": el.text, **box}
if not el.auto_width:
args["width"] = _round(el.width)
args.update(font_family=el.font_family, font_size=_round(el.font_size),
color=el.color or "#000000")
if el.align != "left":
args["align"] = el.align
for key in ("bold", "italic", "underline", "uppercase"):
if getattr(el, key):
args[key] = True
if el.line_height != 1.0:
args["line_height"] = _round(el.line_height)
if el.letter_spacing:
args["letter_spacing"] = _round(el.letter_spacing)
if el.runs:
args["runs"] = [r.model_dump(exclude_none=True) for r in el.runs]
args.update(extra)
return "add_text", args
def tool_calls(doc: DesignDoc) -> list[dict]:
calls = []
if doc.background.lower() != "#ffffff":
calls.append({"name": "set_background", "arguments": {"color": doc.background}})
for el in doc.elements:
if not el.visible:
continue
name, args = element_call(el)
calls.append({"name": name, "arguments": args})
calls.append({"name": "submit", "arguments": {}})
return calls
def html_page(doc: DesignDoc) -> str:
return doc_to_html(doc)
|