fffiloni commited on
Commit
9719a41
·
verified ·
1 Parent(s): 3d7a15c

Upload 3 files

Browse files
Files changed (3) hide show
  1. CHANGELOG.md +8 -0
  2. README.md +5 -2
  3. app.py +174 -103
CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
  # Changelog
2
 
 
 
 
 
 
 
 
 
3
  ## v17-product-button-links
4
 
5
  - Replaced custom HTML/Markdown quick links with native Gradio `gr.Button(link=..., link_target="_blank")` controls.
 
1
  # Changelog
2
 
3
+ ## v18-product-polished-ui
4
+
5
+ - Refined the public Gradio UI without changing backend workflows.
6
+ - Added a centered max-width layout and lightweight card styling.
7
+ - Reorganized Build and Validate inputs into two-column sections where appropriate.
8
+ - Grouped advanced settings and smoke-test payloads into clearer sections.
9
+ - Kept native Gradio quick-link buttons for Job, Space, Settings, and artifacts.
10
+
11
  ## v17-product-button-links
12
 
13
  - Replaced custom HTML/Markdown quick links with native Gradio `gr.Button(link=..., link_target="_blank")` controls.
README.md CHANGED
@@ -26,6 +26,9 @@ model card → agentic job → generated private Space → hardware if available
26
 
27
  ## Main workflow
28
 
 
 
 
29
  ### 1. Build from model card
30
 
31
  Paste a Hugging Face model ID or model-card URL. The app launches an HF Job that:
@@ -37,7 +40,7 @@ Paste a Hugging Face model ID or model-card URL. The app launches an HF Job that
37
  - attempts ZeroGPU first, then fixed-GPU fallback if enabled;
38
  - validates `/health` or Gradio API availability;
39
  - classifies the result honestly;
40
- - returns quick links to the Job page, generated Space, Space settings, and run artifacts.
41
 
42
  ### 2. Validate existing Space
43
 
@@ -49,7 +52,7 @@ If automatic hardware assignment fails, set the hardware manually in the generat
49
  - stores returned artifacts in the Bucket;
50
  - measures latency;
51
  - recommends a conservative ZeroGPU duration;
52
- - returns quick links to the validation Job, target Space, Space settings, and run artifacts.
53
 
54
  ## Result statuses
55
 
 
26
 
27
  ## Main workflow
28
 
29
+ The public UI is intentionally compact: Build and Validate are the only product workflows, with inputs grouped into two-column cards and advanced settings separated from the primary fields.
30
+
31
+
32
  ### 1. Build from model card
33
 
34
  Paste a Hugging Face model ID or model-card URL. The app launches an HF Job that:
 
40
  - attempts ZeroGPU first, then fixed-GPU fallback if enabled;
41
  - validates `/health` or Gradio API availability;
42
  - classifies the result honestly;
43
+ - returns native Gradio buttons to open the Job page, generated Space, Space settings, and run artifacts.
44
 
45
  ### 2. Validate existing Space
46
 
 
52
  - stores returned artifacts in the Bucket;
53
  - measures latency;
54
  - recommends a conservative ZeroGPU duration;
55
+ - returns native Gradio buttons to open the validation Job, target Space, Space settings, and run artifacts.
56
 
57
  ## Result statuses
58
 
app.py CHANGED
@@ -17,6 +17,34 @@ from src.runs import make_run_id, validate_run_id
17
  from src.security import redact
18
 
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  APP_DESCRIPTION = f"""
21
  # Agentic Space Factory
22
 
@@ -242,70 +270,93 @@ def refresh_run_ui(
242
 
243
 
244
  def build_demo() -> gr.Blocks:
245
- with gr.Blocks(title="Agentic Space Factory") as demo:
246
- gr.Markdown(APP_DESCRIPTION)
247
- gr.LoginButton()
248
-
249
- login_status = gr.Markdown()
 
 
250
  demo.load(fn=get_login_status, inputs=None, outputs=login_status)
251
 
252
  with gr.Tab("Build from model card"):
253
  gr.Markdown(
254
  """
255
- Paste a Hugging Face model ID or model-card URL. The worker creates a **private** Space, asks Pi + Qwen Coder to build the best Gradio app it can, attempts ZeroGPU first, then a fixed-GPU fallback if enabled. If automatic hardware assignment fails, set the hardware manually in the generated Space settings and run the validation tab.
256
- """
257
- )
258
- with gr.Row():
259
- build_run_id = gr.Textbox(label="Run ID", value=propose_universal_run_id, interactive=True)
260
- gr.Button("Generate new run id").click(fn=propose_universal_run_id, inputs=None, outputs=build_run_id)
261
-
262
- model_id = gr.Textbox(
263
- label="Model card URL or model ID",
264
- value="Tongyi-MAI/Z-Image-Turbo",
265
- info="Examples: owner/model, https://huggingface.co/owner/model",
266
  )
267
- target_space_name = gr.Textbox(
268
- label="Target Space name",
269
- placeholder="e.g. space-factory-z-image-v1",
270
- info="Use a fresh name. The Space is created under your username and remains private.",
271
- )
272
- pi_model = gr.Textbox(
273
- label="Pi model",
274
- value="Qwen/Qwen3-Coder-Next",
275
- info="Model used by Pi through Hugging Face Inference Providers.",
276
- )
277
- implementation_mode = gr.Dropdown(
278
- label="Implementation mode",
279
- choices=["full-inference-gated", "full-inference-attempt", "safe-scaffold"],
280
- value="full-inference-gated",
281
- info="Gated mode forbids placeholder success; impossible models must produce technical blockers.",
282
- )
283
- with gr.Row():
284
- preferred_hw = gr.Dropdown(
285
- label="Preferred Space hardware",
286
- choices=["zero-a10g", "cpu-basic", "t4-small", "t4-medium", "a10g-large", "l40sx1", "a100-large", "h200"],
287
- value="zero-a10g",
288
- info="ZeroGPU is attempted first by the worker. If your quota is exceeded, use manual hardware selection after generation.",
289
- )
290
- allow_fallback = gr.Checkbox(label="Allow fixed GPU fallback", value=True)
291
- fallback_hw = gr.Dropdown(
292
- label="Fallback Space hardware",
293
- choices=["l40sx1", "a10g-large", "a100-large", "h200", "t4-medium"],
294
- value="l40sx1",
295
- )
296
-
297
- build_btn = gr.Button("Build private Space", variant="primary")
298
- build_job_id = gr.Textbox(label="Job ID", interactive=True)
299
- build_job_url = gr.Textbox(label="Job URL", interactive=False)
300
- generated_space = gr.Textbox(label="Generated Space", interactive=False)
301
- generated_space_url = gr.Textbox(label="Generated Space URL", interactive=False)
302
- gr.Markdown("Quick links")
303
- with gr.Row():
304
- build_job_button = gr.Button("Open HF Job ↗", link=None, link_target="_blank", visible=False)
305
- build_space_button = gr.Button("Open target Space ↗", link=None, link_target="_blank", visible=False)
306
- build_settings_button = gr.Button("Open Space settings ↗", link=None, link_target="_blank", visible=False)
307
- build_artifacts_button = gr.Button("Open run artifacts ", link=None, link_target="_blank", visible=False)
308
- build_result = gr.Code(label="Launch result", language="json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
 
310
  build_btn.click(
311
  fn=launch_universal_model_card_job_ui,
@@ -324,50 +375,67 @@ Paste a Hugging Face model ID or model-card URL. The worker creates a **private*
324
  ],
325
  )
326
 
327
- build_refresh = gr.Button("Refresh build run status")
328
- with gr.Tab("Build state"):
329
- build_state = gr.Code(label="state.json", language="json")
330
- with gr.Tab("Build events"):
331
- build_events = gr.Code(label="events.jsonl", language="json")
332
- with gr.Tab("Build report"):
333
- build_report = gr.Markdown()
334
- with gr.Tab("Build job"):
335
- build_job_info = gr.Code(label="Job info/logs", language="json")
 
 
 
336
 
337
  build_refresh.click(fn=refresh_run_ui, inputs=[build_run_id, build_job_id], outputs=[build_state, build_events, build_report, build_job_info])
338
 
339
  with gr.Tab("Validate existing Space"):
340
  gr.Markdown(
341
  """
 
342
  Use this after the builder generated a Space, especially if you had to set the GPU manually. This job does not rerun Pi. It waits for the existing Space, calls a live generation endpoint, checks the output type, stores returned artifacts in the Bucket, measures latency, and recommends a conservative ZeroGPU duration.
343
- """
 
344
  )
345
- with gr.Row():
346
- validate_run_id = gr.Textbox(label="Run ID", value=propose_validate_run_id, interactive=True)
347
- gr.Button("Generate new validation run id").click(fn=propose_validate_run_id, inputs=None, outputs=validate_run_id)
348
 
349
- target_space = gr.Textbox(
350
- label="Existing target Space",
351
- placeholder="fffiloni/space-factory-... or https://huggingface.co/spaces/...",
352
- )
353
- with gr.Row():
354
- api_name = gr.Textbox(label="Generation API name", value="/generate")
355
- expected_type = gr.Dropdown(label="Expected output type", choices=["image", "video", "audio", "text", "any"], value="image")
356
- test_args = gr.Code(label="Test args JSON list", language="json", value='["a cinematic robot cat astronaut, detailed, studio lighting"]')
357
- test_kwargs = gr.Code(label="Test kwargs JSON object", language="json", value="{}")
358
- timeout_s = gr.Number(label="Live wait timeout seconds", value=1800, precision=0)
359
-
360
- validate_btn = gr.Button("Validate Space + smoke-test generation", variant="primary")
361
- validate_job_id = gr.Textbox(label="Job ID", interactive=True)
362
- validate_job_url = gr.Textbox(label="Job URL", interactive=False)
363
- validate_space_url = gr.Textbox(label="Target Space URL", interactive=False)
364
- gr.Markdown("Quick links")
365
- with gr.Row():
366
- validate_job_button = gr.Button("Open HF Job ", link=None, link_target="_blank", visible=False)
367
- validate_space_button = gr.Button("Open target Space ", link=None, link_target="_blank", visible=False)
368
- validate_settings_button = gr.Button("Open Space settings ", link=None, link_target="_blank", visible=False)
369
- validate_artifacts_button = gr.Button("Open run artifacts ↗", link=None, link_target="_blank", visible=False)
370
- validate_result = gr.Code(label="Launch result", language="json")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
371
 
372
  validate_btn.click(
373
  fn=launch_validate_existing_space_job_ui,
@@ -385,15 +453,17 @@ Use this after the builder generated a Space, especially if you had to set the G
385
  ],
386
  )
387
 
388
- validate_refresh = gr.Button("Refresh validation run status")
389
- with gr.Tab("Validation state"):
390
- validate_state = gr.Code(label="state.json", language="json")
391
- with gr.Tab("Validation events"):
392
- validate_events = gr.Code(label="events.jsonl", language="json")
393
- with gr.Tab("Validation report"):
394
- validate_report = gr.Markdown()
395
- with gr.Tab("Validation job"):
396
- validate_job_info = gr.Code(label="Job info/logs", language="json")
 
 
397
 
398
  validate_refresh.click(fn=refresh_run_ui, inputs=[validate_run_id, validate_job_id], outputs=[validate_state, validate_events, validate_report, validate_job_info])
399
 
@@ -416,7 +486,8 @@ The builder tries to create an app optimized for ZeroGPU when GPU is needed. It
416
  ## What this app cannot guarantee
417
 
418
  It cannot guarantee that every model card becomes a working Space. It cannot bypass model licenses, ZeroGPU quota, billing requirements, custom CUDA build failures, multi-GPU needs, or missing model documentation.
419
- """
 
420
  )
421
 
422
  return demo
 
17
  from src.security import redact
18
 
19
 
20
+
21
+
22
+ APP_CSS = """
23
+ .gradio-container {
24
+ max-width: 1180px !important;
25
+ margin: 0 auto !important;
26
+ }
27
+ .asf-hero {
28
+ max-width: 920px;
29
+ margin: 0 auto 1rem auto;
30
+ }
31
+ .asf-card {
32
+ border: 1px solid var(--border-color-primary);
33
+ border-radius: 14px;
34
+ padding: 1rem;
35
+ background: var(--background-fill-secondary);
36
+ }
37
+ .asf-compact textarea, .asf-compact input {
38
+ font-size: 0.95rem;
39
+ }
40
+ .asf-links button {
41
+ min-width: 0 !important;
42
+ }
43
+ .asf-status textarea {
44
+ font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
45
+ }
46
+ """
47
+
48
  APP_DESCRIPTION = f"""
49
  # Agentic Space Factory
50
 
 
270
 
271
 
272
  def build_demo() -> gr.Blocks:
273
+ with gr.Blocks(title="Agentic Space Factory", css=APP_CSS) as demo:
274
+ gr.Markdown(APP_DESCRIPTION, elem_classes=["asf-hero"])
275
+ with gr.Row(equal_height=True):
276
+ with gr.Column(scale=0, min_width=220):
277
+ gr.LoginButton()
278
+ with gr.Column(scale=1):
279
+ login_status = gr.Markdown(elem_classes=["asf-card"])
280
  demo.load(fn=get_login_status, inputs=None, outputs=login_status)
281
 
282
  with gr.Tab("Build from model card"):
283
  gr.Markdown(
284
  """
285
+ ### Build a private Space
286
+ Paste a Hugging Face model ID or model-card URL. The worker creates a private Space, asks Pi + Qwen Coder to build the app, attempts ZeroGPU first, then a fixed-GPU fallback if enabled. If automatic hardware assignment fails, set the hardware manually in the generated Space settings and run the validation tab.
287
+ """,
288
+ elem_classes=["asf-hero"],
 
 
 
 
 
 
 
289
  )
290
+
291
+ with gr.Row(equal_height=True):
292
+ with gr.Column(scale=2):
293
+ with gr.Group(elem_classes=["asf-card"]):
294
+ gr.Markdown("#### Model and target")
295
+ with gr.Row():
296
+ model_id = gr.Textbox(
297
+ label="Model card URL or model ID",
298
+ value="Tongyi-MAI/Z-Image-Turbo",
299
+ info="Examples: owner/model, https://huggingface.co/owner/model",
300
+ scale=2,
301
+ )
302
+ target_space_name = gr.Textbox(
303
+ label="Target Space name",
304
+ placeholder="e.g. space-factory-z-image-v1",
305
+ info="Created under your username and kept private.",
306
+ scale=1,
307
+ )
308
+ with gr.Row():
309
+ build_run_id = gr.Textbox(label="Run ID", value=propose_universal_run_id, interactive=True, scale=2)
310
+ gr.Button("Generate new run id", scale=0).click(fn=propose_universal_run_id, inputs=None, outputs=build_run_id)
311
+
312
+ with gr.Accordion("Agent and implementation settings", open=True):
313
+ with gr.Row():
314
+ pi_model = gr.Textbox(
315
+ label="Pi model",
316
+ value="Qwen/Qwen3-Coder-Next",
317
+ info="Model used by Pi through Hugging Face Inference Providers.",
318
+ scale=1,
319
+ )
320
+ implementation_mode = gr.Dropdown(
321
+ label="Implementation mode",
322
+ choices=["full-inference-gated", "full-inference-attempt", "safe-scaffold"],
323
+ value="full-inference-gated",
324
+ info="Gated mode forbids placeholder success; impossible models must produce technical blockers.",
325
+ scale=1,
326
+ )
327
+ with gr.Row():
328
+ preferred_hw = gr.Dropdown(
329
+ label="Preferred Space hardware",
330
+ choices=["zero-a10g", "cpu-basic", "t4-small", "t4-medium", "a10g-large", "l40sx1", "a100-large", "h200"],
331
+ value="zero-a10g",
332
+ info="ZeroGPU is attempted first. If quota is exceeded, use manual hardware selection after generation.",
333
+ scale=1,
334
+ )
335
+ fallback_hw = gr.Dropdown(
336
+ label="Fallback Space hardware",
337
+ choices=["l40sx1", "a10g-large", "a100-large", "h200", "t4-medium"],
338
+ value="l40sx1",
339
+ scale=1,
340
+ )
341
+ allow_fallback = gr.Checkbox(label="Allow fixed GPU fallback", value=True, scale=0)
342
+
343
+ with gr.Column(scale=1, min_width=280):
344
+ with gr.Group(elem_classes=["asf-card"]):
345
+ gr.Markdown("#### Launch")
346
+ build_btn = gr.Button("Build private Space", variant="primary")
347
+ build_job_id = gr.Textbox(label="Job ID", interactive=True)
348
+ build_job_url = gr.Textbox(label="Job URL", interactive=False, visible=False)
349
+ generated_space = gr.Textbox(label="Generated Space", interactive=False)
350
+ generated_space_url = gr.Textbox(label="Generated Space URL", interactive=False, visible=False)
351
+ gr.Markdown("Quick links")
352
+ with gr.Row(elem_classes=["asf-links"]):
353
+ build_job_button = gr.Button("Open HF Job ↗", link=None, link_target="_blank", visible=False)
354
+ build_space_button = gr.Button("Open target Space ↗", link=None, link_target="_blank", visible=False)
355
+ with gr.Row(elem_classes=["asf-links"]):
356
+ build_settings_button = gr.Button("Open Space settings ↗", link=None, link_target="_blank", visible=False)
357
+ build_artifacts_button = gr.Button("Open run artifacts ↗", link=None, link_target="_blank", visible=False)
358
+
359
+ build_result = gr.Code(label="Launch result", language="json", elem_classes=["asf-compact"])
360
 
361
  build_btn.click(
362
  fn=launch_universal_model_card_job_ui,
 
375
  ],
376
  )
377
 
378
+ with gr.Group(elem_classes=["asf-card"]):
379
+ with gr.Row():
380
+ build_refresh = gr.Button("Refresh build run status", variant="secondary")
381
+ with gr.Tabs():
382
+ with gr.Tab("State"):
383
+ build_state = gr.Code(label="state.json", language="json", elem_classes=["asf-status"])
384
+ with gr.Tab("Events"):
385
+ build_events = gr.Code(label="events.jsonl", language="json", elem_classes=["asf-status"])
386
+ with gr.Tab("Report"):
387
+ build_report = gr.Markdown()
388
+ with gr.Tab("Job logs"):
389
+ build_job_info = gr.Code(label="Job info/logs", language="json", elem_classes=["asf-status"])
390
 
391
  build_refresh.click(fn=refresh_run_ui, inputs=[build_run_id, build_job_id], outputs=[build_state, build_events, build_report, build_job_info])
392
 
393
  with gr.Tab("Validate existing Space"):
394
  gr.Markdown(
395
  """
396
+ ### Validate a generated Space
397
  Use this after the builder generated a Space, especially if you had to set the GPU manually. This job does not rerun Pi. It waits for the existing Space, calls a live generation endpoint, checks the output type, stores returned artifacts in the Bucket, measures latency, and recommends a conservative ZeroGPU duration.
398
+ """,
399
+ elem_classes=["asf-hero"],
400
  )
 
 
 
401
 
402
+ with gr.Row(equal_height=True):
403
+ with gr.Column(scale=2):
404
+ with gr.Group(elem_classes=["asf-card"]):
405
+ gr.Markdown("#### Target and endpoint")
406
+ target_space = gr.Textbox(
407
+ label="Existing target Space",
408
+ placeholder="fffiloni/space-factory-... or https://huggingface.co/spaces/...",
409
+ )
410
+ with gr.Row():
411
+ api_name = gr.Textbox(label="Generation API name", value="/generate", scale=1)
412
+ expected_type = gr.Dropdown(label="Expected output type", choices=["image", "video", "audio", "text", "any"], value="image", scale=1)
413
+ with gr.Row():
414
+ validate_run_id = gr.Textbox(label="Run ID", value=propose_validate_run_id, interactive=True, scale=2)
415
+ gr.Button("Generate new validation run id", scale=0).click(fn=propose_validate_run_id, inputs=None, outputs=validate_run_id)
416
+
417
+ with gr.Accordion("Smoke-test payload", open=True):
418
+ with gr.Row():
419
+ test_args = gr.Code(label="Test args JSON list", language="json", value='["a cinematic robot cat astronaut, detailed, studio lighting"]', scale=1)
420
+ test_kwargs = gr.Code(label="Test kwargs JSON object", language="json", value="{}", scale=1)
421
+ timeout_s = gr.Number(label="Live wait timeout seconds", value=1800, precision=0)
422
+
423
+ with gr.Column(scale=1, min_width=280):
424
+ with gr.Group(elem_classes=["asf-card"]):
425
+ gr.Markdown("#### Launch")
426
+ validate_btn = gr.Button("Validate Space + smoke-test generation", variant="primary")
427
+ validate_job_id = gr.Textbox(label="Job ID", interactive=True)
428
+ validate_job_url = gr.Textbox(label="Job URL", interactive=False, visible=False)
429
+ validate_space_url = gr.Textbox(label="Target Space URL", interactive=False, visible=False)
430
+ gr.Markdown("Quick links")
431
+ with gr.Row(elem_classes=["asf-links"]):
432
+ validate_job_button = gr.Button("Open HF Job ↗", link=None, link_target="_blank", visible=False)
433
+ validate_space_button = gr.Button("Open target Space ↗", link=None, link_target="_blank", visible=False)
434
+ with gr.Row(elem_classes=["asf-links"]):
435
+ validate_settings_button = gr.Button("Open Space settings ↗", link=None, link_target="_blank", visible=False)
436
+ validate_artifacts_button = gr.Button("Open run artifacts ↗", link=None, link_target="_blank", visible=False)
437
+
438
+ validate_result = gr.Code(label="Launch result", language="json", elem_classes=["asf-compact"])
439
 
440
  validate_btn.click(
441
  fn=launch_validate_existing_space_job_ui,
 
453
  ],
454
  )
455
 
456
+ with gr.Group(elem_classes=["asf-card"]):
457
+ validate_refresh = gr.Button("Refresh validation run status", variant="secondary")
458
+ with gr.Tabs():
459
+ with gr.Tab("State"):
460
+ validate_state = gr.Code(label="state.json", language="json", elem_classes=["asf-status"])
461
+ with gr.Tab("Events"):
462
+ validate_events = gr.Code(label="events.jsonl", language="json", elem_classes=["asf-status"])
463
+ with gr.Tab("Report"):
464
+ validate_report = gr.Markdown()
465
+ with gr.Tab("Job logs"):
466
+ validate_job_info = gr.Code(label="Job info/logs", language="json", elem_classes=["asf-status"])
467
 
468
  validate_refresh.click(fn=refresh_run_ui, inputs=[validate_run_id, validate_job_id], outputs=[validate_state, validate_events, validate_report, validate_job_info])
469
 
 
486
  ## What this app cannot guarantee
487
 
488
  It cannot guarantee that every model card becomes a working Space. It cannot bypass model licenses, ZeroGPU quota, billing requirements, custom CUDA build failures, multi-GPU needs, or missing model documentation.
489
+ """,
490
+ elem_classes=["asf-hero"],
491
  )
492
 
493
  return demo