AdithyaSK HF Staff commited on
Commit
23df34a
·
verified ·
1 Parent(s): cd5c830

Serve Crello corpus aafdb0e86816 from the mounted bucket

Browse files
Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- # Design Canvas: OpenEnv server + editor + headless Chromium for the GUI surface.
2
  FROM python:3.12-slim
3
 
4
  ENV PYTHONUNBUFFERED=1 UV_LINK_MODE=copy PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
 
1
+ # DesignGym: OpenEnv server + editor + headless Chromium for the GUI surface.
2
  FROM python:3.12-slim
3
 
4
  ENV PYTHONUNBUFFERED=1 UV_LINK_MODE=copy PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: Design Canvas
3
  emoji: 🎨
4
  colorFrom: indigo
5
  colorTo: pink
@@ -11,13 +11,16 @@ tags: [openenv, mcp, computer-use, graphic-design, crello]
11
  short_description: Recreate real graphic designs via MCP, HTML or a real editor
12
  ---
13
 
14
- # Design Canvas
15
 
16
  An [OpenEnv](https://github.com/huggingface/OpenEnv) environment for rebuilding real graphic designs from
17
  their parts. Each task is a [Crello](https://huggingface.co/datasets/cyberagent/crello) template taken apart
18
  into its pre-rendered layers and its typed copy. The agent puts it back together on a canvas and is scored
19
  against a render of the original.
20
 
 
 
 
21
  - **Two tasks.** `mode="reference"`: here is the target image, recreate it. `mode="description"`: here is a
22
  written brief naming every asset and line of copy, build it.
23
  - **Three surfaces over one document.** `surface="tools"`: structured MCP edits. `surface="html"`: one page of
@@ -92,7 +95,7 @@ Measured locally (`scripts/loadtest.py`, 4 workers, no errors):
92
 
93
  The router itself saturates one core at about 300–450 tool calls per second, so 4–6 workers is the useful range.
94
 
95
- On a private Space (`scripts/deploy_space.py`: cpu-upgrade, 8 vCPU / 32 GB, 4 workers × 48 sessions, the full
96
  corpus read over HTTP), tested from India, where the network round trip is about 0.22 s:
97
 
98
  | Load | Result | Throughput | Step p50 / p95 |
 
1
  ---
2
+ title: DesignGym
3
  emoji: 🎨
4
  colorFrom: indigo
5
  colorTo: pink
 
11
  short_description: Recreate real graphic designs via MCP, HTML or a real editor
12
  ---
13
 
14
+ # DesignGym
15
 
16
  An [OpenEnv](https://github.com/huggingface/OpenEnv) environment for rebuilding real graphic designs from
17
  their parts. Each task is a [Crello](https://huggingface.co/datasets/cyberagent/crello) template taken apart
18
  into its pre-rendered layers and its typed copy. The agent puts it back together on a canvas and is scored
19
  against a render of the original.
20
 
21
+ The package, the CLI, the OpenEnv name and the `DESIGN_CANVAS_*` settings keep the id `design_canvas`
22
+ (`design-canvas`). Clients, datasets and traces already use it.
23
+
24
  - **Two tasks.** `mode="reference"`: here is the target image, recreate it. `mode="description"`: here is a
25
  written brief naming every asset and line of copy, build it.
26
  - **Three surfaces over one document.** `surface="tools"`: structured MCP edits. `surface="html"`: one page of
 
95
 
96
  The router itself saturates one core at about 300–450 tool calls per second, so 4–6 workers is the useful range.
97
 
98
+ On the private Space [FineEnvs/DesignGym](https://huggingface.co/spaces/FineEnvs/DesignGym) (`scripts/deploy_space.py`: cpu-upgrade, 8 vCPU / 32 GB, 4 workers × 48 sessions, the full
99
  corpus read over HTTP), tested from India, where the network round trip is about 0.22 s:
100
 
101
  | Load | Result | Throughput | Step p50 / p95 |
__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- """Design Canvas: recreate graphic designs in a real editor."""
2
 
3
  __version__ = "0.1.0"
 
1
+ """DesignGym (package design_canvas): recreate graphic designs in a real editor."""
2
 
3
  __version__ = "0.1.0"
cli.py CHANGED
@@ -67,7 +67,7 @@ def cmd_serve(args) -> int:
67
  os.environ["PORT"] = str(args.port)
68
  from .server.app import main
69
 
70
- print(f"Design Canvas on http://{args.host}:{args.port} "
71
  f"(editor /editor/, playground /web/, MCP /mcp)")
72
  main(host=args.host, port=args.port, workers=args.workers)
73
  return 0
 
67
  os.environ["PORT"] = str(args.port)
68
  from .server.app import main
69
 
70
+ print(f"DesignGym on http://{args.host}:{args.port} "
71
  f"(editor /editor/, playground /web/, MCP /mcp)")
72
  main(host=args.host, port=args.port, workers=args.workers)
73
  return 0
scripts/deploy_space.py CHANGED
@@ -4,7 +4,7 @@ The Space image holds code and the corpus manifest only. The catalog is copied f
4
  each design's pixels are read from it the first time an episode uses it: over HTTP by default, or with
5
  --transport mount from the bucket attached read-only at /corpus.
6
 
7
- python scripts/deploy_space.py --space FineEnvs/design-canvas \\
8
  --manifest corpus-manifest.json --private
9
 
10
  Refuses to deploy unless the manifest's catalog is published in the bucket
@@ -29,7 +29,7 @@ def main(argv=None) -> int:
29
  from huggingface_hub import HfApi, Volume
30
 
31
  ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
32
- ap.add_argument("--space", required=True, help="e.g. FineEnvs/design-canvas")
33
  ap.add_argument("--manifest", default=str(HERE / "corpus-manifest.json"))
34
  ap.add_argument("--hardware", default="cpu-upgrade", help="Space hardware (GUI episodes run Chromium)")
35
  ap.add_argument("--private", action="store_true")
 
4
  each design's pixels are read from it the first time an episode uses it: over HTTP by default, or with
5
  --transport mount from the bucket attached read-only at /corpus.
6
 
7
+ python scripts/deploy_space.py --space FineEnvs/DesignGym \\
8
  --manifest corpus-manifest.json --private
9
 
10
  Refuses to deploy unless the manifest's catalog is published in the bucket
 
29
  from huggingface_hub import HfApi, Volume
30
 
31
  ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
32
+ ap.add_argument("--space", required=True, help="e.g. FineEnvs/DesignGym")
33
  ap.add_argument("--manifest", default=str(HERE / "corpus-manifest.json"))
34
  ap.add_argument("--hardware", default="cpu-upgrade", help="Space hardware (GUI episodes run Chromium)")
35
  ap.add_argument("--private", action="store_true")
server/app.py CHANGED
@@ -46,10 +46,10 @@ def create_server():
46
  env_name=ENV_NAME,
47
  max_concurrent_envs=int(os.environ.get("MAX_CONCURRENT_ENVS", "64")),
48
  gradio_builder=gradio_builder,
49
- custom_tab_name="Design Canvas",
50
  custom_tab_primary=True,
51
  show_default_tab=True,
52
- title_override="Design Canvas · OpenEnv",
53
  )
54
  mount_editor(app, pack)
55
 
 
46
  env_name=ENV_NAME,
47
  max_concurrent_envs=int(os.environ.get("MAX_CONCURRENT_ENVS", "64")),
48
  gradio_builder=gradio_builder,
49
+ custom_tab_name="DesignGym",
50
  custom_tab_primary=True,
51
  show_default_tab=True,
52
+ title_override="DesignGym · OpenEnv",
53
  )
54
  mount_editor(app, pack)
55
 
server/gradio_ui.py CHANGED
@@ -151,7 +151,7 @@ def build_ui(pack: TaskPack) -> gr.Blocks:
151
  "opacity:.6;border:1px dashed #555;border-radius:8px'>Start a task to open the "
152
  "editor.</div>")
153
  src = f"/editor/?episode={ep_id}&skin={skin}"
154
- return (f"<div style='width:100%'><iframe src='{src}' title='Design Canvas editor' "
155
  f"style='display:block;width:100%;height:calc(100vh - 120px);min-height:640px;"
156
  f"border:1px solid #444;border-radius:8px;"
157
  f"background:#fff' allow='clipboard-write'></iframe>"
@@ -363,7 +363,7 @@ def build_ui(pack: TaskPack) -> gr.Blocks:
363
  gr.HTML("<style>.gradio-container{max-width:100% !important;padding-left:16px !important;"
364
  "padding-right:16px !important}.gradio-container .main,.gradio-container .wrap"
365
  "{max-width:100% !important}</style>", padding=False)
366
- gr.Markdown("# Design Canvas — try it")
367
  gr.Markdown(INTRO)
368
  episode = gr.State(None)
369
  with gr.Row():
 
151
  "opacity:.6;border:1px dashed #555;border-radius:8px'>Start a task to open the "
152
  "editor.</div>")
153
  src = f"/editor/?episode={ep_id}&skin={skin}"
154
+ return (f"<div style='width:100%'><iframe src='{src}' title='DesignGym editor' "
155
  f"style='display:block;width:100%;height:calc(100vh - 120px);min-height:640px;"
156
  f"border:1px solid #444;border-radius:8px;"
157
  f"background:#fff' allow='clipboard-write'></iframe>"
 
363
  gr.HTML("<style>.gradio-container{max-width:100% !important;padding-left:16px !important;"
364
  "padding-right:16px !important}.gradio-container .main,.gradio-container .wrap"
365
  "{max-width:100% !important}</style>", padding=False)
366
+ gr.Markdown("# DesignGym — try it")
367
  gr.Markdown(INTRO)
368
  episode = gr.State(None)
369
  with gr.Row():
server/router.py CHANGED
@@ -299,7 +299,7 @@ def serve(host: str, port: int, workers: int) -> None:
299
  try:
300
  for u in urls:
301
  _wait_healthy(u, procs)
302
- print(f"Design Canvas on http://{host}:{port}: {workers} workers on ports {base}-{base + workers - 1}",
303
  flush=True)
304
  uvicorn.run(build_app(urls), host=host, port=port, ws_ping_interval=60, ws_ping_timeout=300,
305
  log_level="warning")
 
299
  try:
300
  for u in urls:
301
  _wait_healthy(u, procs)
302
+ print(f"DesignGym on http://{host}:{port}: {workers} workers on ports {base}-{base + workers - 1}",
303
  flush=True)
304
  uvicorn.run(build_app(urls), host=host, port=port, ws_ping_interval=60, ws_ping_timeout=300,
305
  log_level="warning")
web/index.html CHANGED
@@ -3,7 +3,7 @@
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Design Canvas</title>
7
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%237d2ae8'/%3E%3Crect x='7' y='7' width='11' height='11' rx='2' fill='white'/%3E%3Ccircle cx='21' cy='21' r='6' fill='%2300c4cc'/%3E%3C/svg%3E">
8
  <link rel="stylesheet" href="css/base.css">
9
  <link rel="stylesheet" href="css/figma.css">
 
3
  <head>
4
  <meta charset="utf-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>DesignGym</title>
7
  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='7' fill='%237d2ae8'/%3E%3Crect x='7' y='7' width='11' height='11' rx='2' fill='white'/%3E%3Ccircle cx='21' cy='21' r='6' fill='%2300c4cc'/%3E%3C/svg%3E">
8
  <link rel="stylesheet" href="css/base.css">
9
  <link rel="stylesheet" href="css/figma.css">
web/js/main.js CHANGED
@@ -57,7 +57,7 @@ class App extends Emitter {
57
  this.store.on("fatal", (msg) => this.fatal(msg));
58
  await this.store.connect();
59
  await ensureFonts([...(this.store.task.fonts_used || []), ...this.store.doc.elements.filter((e) => e.kind === "text").map((e) => e.font_family)]);
60
- document.title = `${this.store.task.title} · Design Canvas`;
61
  this.stage.fit();
62
  this.stage.render();
63
  this.refreshAll();
 
57
  this.store.on("fatal", (msg) => this.fatal(msg));
58
  await this.store.connect();
59
  await ensureFonts([...(this.store.task.fonts_used || []), ...this.store.doc.elements.filter((e) => e.kind === "text").map((e) => e.font_family)]);
60
+ document.title = `${this.store.task.title} · DesignGym`;
61
  this.stage.fit();
62
  this.stage.render();
63
  this.refreshAll();