Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
| 1 |
"""NEXUS Visual Weaver - Build Small Hackathon command center."""
|
| 2 |
-
|
| 3 |
from __future__ import annotations
|
| 4 |
-
|
| 5 |
import os
|
| 6 |
import sys
|
| 7 |
import hashlib
|
|
@@ -9,7 +7,6 @@ import secrets
|
|
| 9 |
from pathlib import Path
|
| 10 |
from typing import Any
|
| 11 |
from urllib.parse import urlparse
|
| 12 |
-
|
| 13 |
import gradio as gr
|
| 14 |
|
| 15 |
ROOT = Path(__file__).resolve().parent
|
|
@@ -18,8 +15,8 @@ if str(SRC) not in sys.path:
|
|
| 18 |
sys.path.insert(0, str(SRC))
|
| 19 |
|
| 20 |
try:
|
| 21 |
-
import spaces
|
| 22 |
-
except Exception:
|
| 23 |
spaces = None
|
| 24 |
|
| 25 |
from nexus_visual_weaver.catalog import catalog_summary
|
|
@@ -28,12 +25,10 @@ from nexus_visual_weaver.hf_runtime import generate_flux_image
|
|
| 28 |
from nexus_visual_weaver.model_relay import WeaverModelRelay
|
| 29 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 30 |
from nexus_visual_weaver.provider_runtime import judge_with_minicpm, judge_with_nemotron
|
| 31 |
-
from nexus_visual_weaver.render import render_catalog_table,
|
| 32 |
from nexus_visual_weaver.security import scan_file
|
| 33 |
from nexus_visual_weaver.styles import APP_CSS
|
| 34 |
|
| 35 |
-
# Use default theme to avoid Gradio 6.18 Font comparison bug
|
| 36 |
-
# Custom theme with any parameter triggers: AttributeError: 'str' object has no attribute 'name'
|
| 37 |
APP_THEME = gr.themes.Soft()
|
| 38 |
|
| 39 |
DEFAULT_PROMPT = (
|
|
@@ -55,7 +50,6 @@ ASPECT_DIMENSIONS = {
|
|
| 55 |
"Portrait": (832, 1216),
|
| 56 |
}
|
| 57 |
|
| 58 |
-
|
| 59 |
def _default_operator_state() -> dict[str, Any]:
|
| 60 |
return {
|
| 61 |
"provider_state": "idle",
|
|
@@ -64,18 +58,15 @@ def _default_operator_state() -> dict[str, Any]:
|
|
| 64 |
"message": "No operator action yet.",
|
| 65 |
}
|
| 66 |
|
| 67 |
-
|
| 68 |
def _zero_gpu_entrypoint(fn: Any) -> Any:
|
| 69 |
gpu_decorator = getattr(spaces, "GPU", None) if spaces is not None else None
|
| 70 |
if gpu_decorator is None:
|
| 71 |
return fn
|
| 72 |
return gpu_decorator(duration=300)(fn)
|
| 73 |
|
| 74 |
-
|
| 75 |
def _relay_snapshot(adult_mode: bool = False) -> dict[str, Any]:
|
| 76 |
return MODEL_RELAY.dashboard_snapshot(public_demo=not adult_mode)
|
| 77 |
|
| 78 |
-
|
| 79 |
def _file_path(uploaded: Any) -> str | None:
|
| 80 |
if uploaded is None:
|
| 81 |
return None
|
|
@@ -84,7 +75,6 @@ def _file_path(uploaded: Any) -> str | None:
|
|
| 84 |
path = getattr(uploaded, "name", None)
|
| 85 |
return str(path) if path else None
|
| 86 |
|
| 87 |
-
|
| 88 |
def _safe_file_hash(path: str | None) -> tuple[str | None, int | None]:
|
| 89 |
if not path:
|
| 90 |
return None, None
|
|
@@ -100,7 +90,6 @@ def _safe_file_hash(path: str | None) -> tuple[str | None, int | None]:
|
|
| 100 |
return None, None
|
| 101 |
return sha256.hexdigest(), size
|
| 102 |
|
| 103 |
-
|
| 104 |
def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] | None:
|
| 105 |
if not reference_url:
|
| 106 |
return None
|
|
@@ -116,30 +105,26 @@ def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] |
|
|
| 116 |
"message": "URL stored as metadata only; Space runtime does not crawl or copy shop images.",
|
| 117 |
}
|
| 118 |
|
| 119 |
-
|
| 120 |
def _reference_metadata(uploaded: Any, reference_url: str | None, scan: dict[str, Any]) -> list[dict[str, Any]]:
|
| 121 |
records: list[dict[str, Any]] = []
|
| 122 |
path = _file_path(uploaded)
|
| 123 |
if path:
|
| 124 |
file_hash, size = _safe_file_hash(path)
|
| 125 |
-
records.append(
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
}
|
| 136 |
-
)
|
| 137 |
url_record = _safe_reference_url_metadata(reference_url)
|
| 138 |
if url_record:
|
| 139 |
records.append(url_record)
|
| 140 |
return records
|
| 141 |
|
| 142 |
-
|
| 143 |
def _creator_controls(
|
| 144 |
reasoning_mode: str,
|
| 145 |
video_preset: str,
|
|
@@ -178,7 +163,6 @@ def _creator_controls(
|
|
| 178 |
},
|
| 179 |
}
|
| 180 |
|
| 181 |
-
|
| 182 |
def _resolve_seed(seed_value: Any) -> int:
|
| 183 |
try:
|
| 184 |
if seed_value is None or str(seed_value).strip() == "":
|
|
@@ -188,15 +172,12 @@ def _resolve_seed(seed_value: Any) -> int:
|
|
| 188 |
return secrets.randbelow(1_000_000_000)
|
| 189 |
return secrets.randbelow(1_000_000_000) if seed < 0 else seed
|
| 190 |
|
| 191 |
-
|
| 192 |
def _generation_dimensions(aspect: str | None) -> tuple[int, int]:
|
| 193 |
return ASPECT_DIMENSIONS.get(str(aspect or "Portrait"), ASPECT_DIMENSIONS["Portrait"])
|
| 194 |
|
| 195 |
-
|
| 196 |
def _style_modifier(style_strength: str | None) -> str:
|
| 197 |
return STYLE_MODIFIERS.get(str(style_strength or "High Fashion"), STYLE_MODIFIERS["High Fashion"])
|
| 198 |
|
| 199 |
-
|
| 200 |
def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
| 201 |
wardrobe = controls.get("wardrobe", {})
|
| 202 |
additions = [
|
|
@@ -215,13 +196,11 @@ def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
|
| 215 |
prompt = f"{prompt}\nWardrobe controls: {suffix}" if suffix else prompt
|
| 216 |
return f"{prompt}\nStyle direction: {style}"
|
| 217 |
|
| 218 |
-
|
| 219 |
def _generated_output_path(operator_state: dict[str, Any] | None) -> str | None:
|
| 220 |
generation = (operator_state or {}).get("generation") or {}
|
| 221 |
output_path = generation.get("output_path")
|
| 222 |
return str(output_path) if output_path else None
|
| 223 |
|
| 224 |
-
|
| 225 |
def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict[str, Any]:
|
| 226 |
output_path = _generated_output_path(operator_state)
|
| 227 |
if output_path:
|
|
@@ -229,7 +208,6 @@ def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict
|
|
| 229 |
stored_scan = (operator_state or {}).get("generated_scan")
|
| 230 |
return stored_scan if isinstance(stored_scan, dict) else scan_file(None)
|
| 231 |
|
| 232 |
-
|
| 233 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
| 234 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 235 |
if not suffix:
|
|
@@ -239,7 +217,6 @@ def _checkpoint_seed(checkpoint_id: str) -> int:
|
|
| 239 |
except ValueError:
|
| 240 |
return 0
|
| 241 |
|
| 242 |
-
|
| 243 |
def _wardrobe_summary(run: Any) -> str:
|
| 244 |
slots = getattr(getattr(run, "outfit", None), "slots", []) or []
|
| 245 |
return "; ".join(
|
|
@@ -247,10 +224,8 @@ def _wardrobe_summary(run: Any) -> str:
|
|
| 247 |
for slot in slots
|
| 248 |
)
|
| 249 |
|
| 250 |
-
|
| 251 |
SECTIONS = ["Forge", "Wardrobe", "Lore", "Models", "Security", "Runs"]
|
| 252 |
|
| 253 |
-
|
| 254 |
def _button_updates(run: Any | None, operator_state: dict[str, Any] | None) -> tuple[Any, Any, Any]:
|
| 255 |
state = operator_state or {}
|
| 256 |
generated = bool(_generated_output_path(state)) and (state.get("generation") or {}).get("status") == "success"
|
|
@@ -262,7 +237,6 @@ def _button_updates(run: Any | None, operator_state: dict[str, Any] | None) -> t
|
|
| 262 |
gr.update(interactive=False),
|
| 263 |
)
|
| 264 |
|
| 265 |
-
|
| 266 |
def _dashboard_regions(
|
| 267 |
run: Any | None = None,
|
| 268 |
adult_mode: bool = False,
|
|
@@ -279,7 +253,6 @@ def _dashboard_regions(
|
|
| 279 |
operator_state=operator_state,
|
| 280 |
)
|
| 281 |
|
| 282 |
-
|
| 283 |
# βββ Modal Integration βββ
|
| 284 |
MODAL_AVAILABLE = False
|
| 285 |
try:
|
|
@@ -288,7 +261,6 @@ try:
|
|
| 288 |
except ImportError:
|
| 289 |
pass
|
| 290 |
|
| 291 |
-
# LoRA Registry (mirrors modal_nexus_refine_v2.py)
|
| 292 |
LORA_ADAPTERS = {
|
| 293 |
"garment": {"repo": "NO8D/BodyControl", "desc": "Body/garment shape control", "weight": 0.75},
|
| 294 |
"hardware": {"repo": "NO8D/ExpressionControl", "desc": "Expression/hardware detail", "weight": 0.70},
|
|
@@ -307,15 +279,12 @@ GPU_OPTIONS = {
|
|
| 307 |
|
| 308 |
MODAL_COST_TRACKER = {"credits_remaining": 250.88, "total_spent": 0.0, "refinements": 0}
|
| 309 |
|
| 310 |
-
|
| 311 |
def _modal_refine_image(image_bytes: bytes, user_addition: str, gpu_type: str = "A100-80GB",
|
| 312 |
strength: float = 0.58, steps: int = 32, guidance_scale: float = 3.8,
|
| 313 |
seed: int = -1, lora_adapters: list | None = None,
|
| 314 |
negative_prompt: str = "blurry, low quality, deformed, extra limbs") -> tuple:
|
| 315 |
-
"""Real Modal refinement call β wires to nexus-couture-refine-v2 on Modal."""
|
| 316 |
if not MODAL_AVAILABLE:
|
| 317 |
-
return None, "β Modal not installed
|
| 318 |
-
|
| 319 |
try:
|
| 320 |
fn = modal.Function.lookup("nexus-couture-refine-v2", "refine_couture")
|
| 321 |
result_bytes = fn.remote(
|
|
@@ -329,28 +298,24 @@ def _modal_refine_image(image_bytes: bytes, user_addition: str, gpu_type: str =
|
|
| 329 |
negative_prompt=negative_prompt,
|
| 330 |
gpu_type=gpu_type,
|
| 331 |
)
|
| 332 |
-
# Update cost tracker
|
| 333 |
gpu_info = GPU_OPTIONS.get(gpu_type, GPU_OPTIONS["A100-80GB"])
|
| 334 |
est_cost = round(gpu_info["price"] * (steps / 60), 4)
|
| 335 |
MODAL_COST_TRACKER["total_spent"] += est_cost
|
| 336 |
MODAL_COST_TRACKER["credits_remaining"] -= est_cost
|
| 337 |
MODAL_COST_TRACKER["refinements"] += 1
|
| 338 |
-
return result_bytes, f"β
Modal refinement complete on {gpu_type}
|
| 339 |
except Exception as e:
|
| 340 |
return None, f"β Modal error: {str(e)[:200]}"
|
| 341 |
|
| 342 |
-
|
| 343 |
def _modal_health_check() -> dict:
|
| 344 |
-
"""Check Modal connectivity and GPU availability."""
|
| 345 |
if not MODAL_AVAILABLE:
|
| 346 |
-
return {"status": "unavailable", "message": "Modal
|
| 347 |
try:
|
| 348 |
fn = modal.Function.lookup("nexus-couture-refine-v2", "check_modal_health")
|
| 349 |
return fn.remote()
|
| 350 |
except Exception as e:
|
| 351 |
return {"status": "error", "message": str(e)[:200]}
|
| 352 |
|
| 353 |
-
|
| 354 |
@_zero_gpu_entrypoint
|
| 355 |
def run_weave(
|
| 356 |
prompt, reasoning_mode, video_preset, adult_mode, upload, active_section,
|
|
@@ -390,6 +355,7 @@ def run_weave(
|
|
| 390 |
provider_state = generation.provider_state
|
| 391 |
else:
|
| 392 |
provider_state = "checkpointed"
|
|
|
|
| 393 |
operator_state = {
|
| 394 |
"provider_state": provider_state,
|
| 395 |
"checkpoint": "pending_review",
|
|
@@ -417,7 +383,6 @@ def run_weave(
|
|
| 417 |
*_button_updates(run, operator_state),
|
| 418 |
)
|
| 419 |
|
| 420 |
-
|
| 421 |
def toggle_adult_visibility(adult_mode, active_section, upload):
|
| 422 |
scan = scan_file(_file_path(upload))
|
| 423 |
operator_state = {
|
|
@@ -431,7 +396,6 @@ def toggle_adult_visibility(adult_mode, active_section, upload):
|
|
| 431 |
render_catalog_table(adult_mode=adult_mode), catalog_summary(adult_mode), scan, operator_state,
|
| 432 |
)
|
| 433 |
|
| 434 |
-
|
| 435 |
def refresh_section(active_section, adult_mode, run, scan, operator_state):
|
| 436 |
scan = scan or scan_file(None)
|
| 437 |
regions = _dashboard_regions(
|
|
@@ -440,7 +404,6 @@ def refresh_section(active_section, adult_mode, run, scan, operator_state):
|
|
| 440 |
)
|
| 441 |
return regions["command_rail"], regions["operations"], regions["inspector"], regions["artifacts"], regions["providers"], scan
|
| 442 |
|
| 443 |
-
|
| 444 |
def _render_stateful(run, adult_mode, scan, active_section, operator_state):
|
| 445 |
scan = scan or scan_file(None)
|
| 446 |
regions = _dashboard_regions(
|
|
@@ -457,7 +420,6 @@ def _render_stateful(run, adult_mode, scan, active_section, operator_state):
|
|
| 457 |
*_button_updates(run, operator_state),
|
| 458 |
)
|
| 459 |
|
| 460 |
-
|
| 461 |
def scan_reference(run, adult_mode, upload, active_section, operator_state, reference_url=None):
|
| 462 |
state = operator_state or _default_operator_state()
|
| 463 |
reference_path = _file_path(upload)
|
|
@@ -486,7 +448,6 @@ def scan_reference(run, adult_mode, upload, active_section, operator_state, refe
|
|
| 486 |
rendered = _render_stateful(run, adult_mode, generated_scan, active_section, next_state)
|
| 487 |
return (*rendered, generated_scan)
|
| 488 |
|
| 489 |
-
|
| 490 |
def approve_checkpoint(run, adult_mode, scan, active_section, operator_state):
|
| 491 |
state = operator_state or _default_operator_state()
|
| 492 |
scan = _authoritative_generated_scan(state)
|
|
@@ -508,7 +469,6 @@ def approve_checkpoint(run, adult_mode, scan, active_section, operator_state):
|
|
| 508 |
}
|
| 509 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 510 |
|
| 511 |
-
|
| 512 |
def export_packet(run, adult_mode, scan, active_section, operator_state, override_reason=None):
|
| 513 |
state = operator_state or _default_operator_state()
|
| 514 |
scan = _authoritative_generated_scan(state)
|
|
@@ -533,7 +493,6 @@ def export_packet(run, adult_mode, scan, active_section, operator_state, overrid
|
|
| 533 |
}
|
| 534 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 535 |
|
| 536 |
-
|
| 537 |
def stop_provider_job(run, adult_mode, scan, active_section, operator_state):
|
| 538 |
scan = scan or scan_file(None)
|
| 539 |
next_state = {
|
|
@@ -543,7 +502,6 @@ def stop_provider_job(run, adult_mode, scan, active_section, operator_state):
|
|
| 543 |
}
|
| 544 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 545 |
|
| 546 |
-
|
| 547 |
def reset_demo(adult_mode, active_section):
|
| 548 |
scan = scan_file(None)
|
| 549 |
operator_state = _default_operator_state()
|
|
@@ -557,11 +515,8 @@ def reset_demo(adult_mode, active_section):
|
|
| 557 |
gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False),
|
| 558 |
)
|
| 559 |
|
| 560 |
-
|
| 561 |
# βββ Modal Tab Handlers βββ
|
| 562 |
-
|
| 563 |
def modal_refine_handler(input_image, user_addition, gpu_type, strength, steps, guidance, seed, lora_choices, negative_prompt):
|
| 564 |
-
"""Handle Modal refinement from the Space UI."""
|
| 565 |
if input_image is None:
|
| 566 |
return None, "β No input image provided"
|
| 567 |
from PIL import Image as PILImage
|
|
@@ -573,7 +528,6 @@ def modal_refine_handler(input_image, user_addition, gpu_type, strength, steps,
|
|
| 573 |
img = PILImage.open(input_image)
|
| 574 |
img.save(buf, format="PNG")
|
| 575 |
image_bytes = buf.getvalue()
|
| 576 |
-
|
| 577 |
result_bytes, message = _modal_refine_image(
|
| 578 |
image_bytes=image_bytes, user_addition=user_addition,
|
| 579 |
gpu_type=gpu_type, strength=strength, steps=int(steps),
|
|
@@ -587,18 +541,15 @@ def modal_refine_handler(input_image, user_addition, gpu_type, strength, steps,
|
|
| 587 |
return result_img, f"{message}\n{cost_info}"
|
| 588 |
return None, message
|
| 589 |
|
| 590 |
-
|
| 591 |
def modal_health_handler():
|
| 592 |
-
"""Check Modal connectivity."""
|
| 593 |
result = _modal_health_check()
|
| 594 |
if result.get("status") == "healthy":
|
| 595 |
-
return f"β
Modal connected\nGPU: {result.get('gpu', 'N/A')}
|
| 596 |
elif result.get("status") == "unavailable":
|
| 597 |
return f"β οΈ {result.get('message', 'Modal not available')}"
|
| 598 |
else:
|
| 599 |
return f"β Modal error: {result.get('message', 'Unknown')}"
|
| 600 |
|
| 601 |
-
|
| 602 |
initial_operator_state = _default_operator_state()
|
| 603 |
initial_regions = _dashboard_regions(scan=scan_file(None), operator_state=initial_operator_state)
|
| 604 |
|
|
@@ -609,7 +560,7 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 609 |
topbar_html = gr.HTML(initial_regions["topbar"], container=False, visible=False)
|
| 610 |
|
| 611 |
with gr.Tabs():
|
| 612 |
-
# βββ Tab 1: Studio
|
| 613 |
with gr.Tab("π§΅ Studio"):
|
| 614 |
with gr.Row(elem_id="nw-creator-workbench", elem_classes=["nw-creator-workbench"]):
|
| 615 |
with gr.Column(scale=5, min_width=520, elem_id="nw-creator-panel"):
|
|
@@ -644,7 +595,6 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 644 |
with gr.Row():
|
| 645 |
scan_btn = gr.Button("Scan Uploaded File", scale=1)
|
| 646 |
stop_btn = gr.Button("Stop Job", variant="stop", interactive=False, scale=1)
|
| 647 |
-
|
| 648 |
with gr.Column(scale=4, min_width=460, elem_id="nw-output-panel"):
|
| 649 |
gr.Markdown("### Output")
|
| 650 |
artifact_html = gr.HTML(initial_regions["artifacts"], container=False)
|
|
@@ -658,7 +608,7 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 658 |
)
|
| 659 |
gr.Markdown("Generation is not export. Every artifact stays behind ST3GG review and human checkpoint.")
|
| 660 |
|
| 661 |
-
# βββ Tab 2: Modal Refinement βββ
|
| 662 |
with gr.Tab("β‘ Modal"):
|
| 663 |
gr.Markdown("## β‘ Modal GPU Refinement")
|
| 664 |
gr.Markdown("Send a generated image to Modal for FLUX.1-Kontext-dev refinement with multi-LoRA on dedicated GPU.")
|
|
@@ -694,7 +644,7 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 694 |
)
|
| 695 |
modal_health_btn.click(fn=modal_health_handler, inputs=[], outputs=[modal_status])
|
| 696 |
|
| 697 |
-
# βββ Tab 3: LoRA Lab βββ
|
| 698 |
with gr.Tab("π§ͺ LoRA Lab"):
|
| 699 |
gr.Markdown("## π§ͺ LoRA Training Lab")
|
| 700 |
gr.Markdown("Train custom LoRA adapters on Modal GPU. Connect a dataset repo and configure training parameters.")
|
|
@@ -719,7 +669,6 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 719 |
for k, v in LORA_ADAPTERS.items()
|
| 720 |
)
|
| 721 |
gr.Markdown(lora_catalog_md)
|
| 722 |
-
|
| 723 |
def lora_train_handler(dataset_repo, output_name, rank, lr, steps, batch, push, hub_repo):
|
| 724 |
if not MODAL_AVAILABLE:
|
| 725 |
return "β Modal not installed"
|
|
@@ -733,7 +682,6 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 733 |
return f"β
Training triggered on Modal!\nDataset: {dataset_repo}\nOutput: {output_name}\nRank: {rank}, Steps: {steps}, LR: {lr}"
|
| 734 |
except Exception as e:
|
| 735 |
return f"β Training error: {str(e)[:300]}"
|
| 736 |
-
|
| 737 |
lora_train_btn.click(
|
| 738 |
fn=lora_train_handler,
|
| 739 |
inputs=[lora_dataset_repo, lora_output_name, lora_rank, lora_lr,
|
|
@@ -781,6 +729,7 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 781 |
outputs=stateful_outputs, api_name="run_active_weave",
|
| 782 |
concurrency_limit=1, concurrency_id="flux-gpu",
|
| 783 |
)
|
|
|
|
| 784 |
run_submit = prompt.submit(
|
| 785 |
fn=run_weave,
|
| 786 |
inputs=[prompt, reasoning_mode, video_preset, adult_mode, upload, section_nav,
|
|
@@ -789,6 +738,7 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 789 |
outputs=stateful_outputs, api_name=False,
|
| 790 |
concurrency_limit=1, concurrency_id="flux-gpu",
|
| 791 |
)
|
|
|
|
| 792 |
adult_mode.change(
|
| 793 |
fn=toggle_adult_visibility,
|
| 794 |
inputs=[adult_mode, section_nav, upload],
|
|
@@ -796,47 +746,53 @@ with gr.Blocks(title="NEXUS Visual Weaver") as demo:
|
|
| 796 |
artifact_html, provider_html, catalog_html, catalog_json, scan_json, operator_state],
|
| 797 |
api_name="toggle_adult_catalog", queue=False,
|
| 798 |
)
|
|
|
|
| 799 |
section_nav.change(
|
| 800 |
fn=refresh_section,
|
| 801 |
inputs=[section_nav, adult_mode, active_run_state, scan_state, operator_state],
|
| 802 |
outputs=[command_rail_html, operations_html, inspector_html, artifact_html, provider_html, scan_json],
|
| 803 |
api_name=False, queue=False,
|
| 804 |
)
|
|
|
|
| 805 |
scan_btn.click(
|
| 806 |
fn=scan_reference,
|
| 807 |
inputs=[active_run_state, adult_mode, upload, section_nav, operator_state, reference_url],
|
| 808 |
outputs=dashboard_outputs + [operator_state, checkpoint_btn, export_btn, stop_btn, scan_state],
|
| 809 |
api_name="scan_reference", queue=False,
|
| 810 |
)
|
|
|
|
| 811 |
checkpoint_btn.click(
|
| 812 |
fn=approve_checkpoint,
|
| 813 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state],
|
| 814 |
outputs=operator_outputs, api_name="approve_checkpoint", queue=False,
|
| 815 |
)
|
|
|
|
| 816 |
export_btn.click(
|
| 817 |
fn=export_packet,
|
| 818 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state, override_reason],
|
| 819 |
outputs=operator_outputs, api_name="prepare_export_packet", queue=False,
|
| 820 |
)
|
|
|
|
| 821 |
stop_btn.click(
|
| 822 |
fn=stop_provider_job,
|
| 823 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state],
|
| 824 |
outputs=operator_outputs, api_name="stop_provider_job", queue=False,
|
| 825 |
cancels=[run_click, run_submit],
|
| 826 |
)
|
|
|
|
| 827 |
reset_btn.click(
|
| 828 |
fn=reset_demo,
|
| 829 |
inputs=[adult_mode, section_nav],
|
| 830 |
outputs=stateful_outputs, api_name="reset_demo_state", queue=False,
|
| 831 |
cancels=[run_click, run_submit],
|
| 832 |
)
|
|
|
|
| 833 |
demo.load(
|
| 834 |
fn=lambda: (render_catalog_table(False), catalog_summary(False), scan_file(None), scan_file(None), _default_operator_state()),
|
| 835 |
outputs=[catalog_html, catalog_json, scan_json, scan_state, operator_state],
|
| 836 |
api_name=False,
|
| 837 |
)
|
| 838 |
|
| 839 |
-
|
| 840 |
if __name__ == "__main__":
|
| 841 |
if hasattr(sys.stdout, "reconfigure"):
|
| 842 |
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
@@ -845,6 +801,8 @@ if __name__ == "__main__":
|
|
| 845 |
demo.launch(
|
| 846 |
server_name="0.0.0.0",
|
| 847 |
server_port=int(os.environ.get("NEXUS_PORT", os.environ.get("PORT", "7860"))),
|
| 848 |
-
quiet=True,
|
|
|
|
|
|
|
| 849 |
css=APP_CSS,
|
| 850 |
-
)
|
|
|
|
| 1 |
"""NEXUS Visual Weaver - Build Small Hackathon command center."""
|
|
|
|
| 2 |
from __future__ import annotations
|
|
|
|
| 3 |
import os
|
| 4 |
import sys
|
| 5 |
import hashlib
|
|
|
|
| 7 |
from pathlib import Path
|
| 8 |
from typing import Any
|
| 9 |
from urllib.parse import urlparse
|
|
|
|
| 10 |
import gradio as gr
|
| 11 |
|
| 12 |
ROOT = Path(__file__).resolve().parent
|
|
|
|
| 15 |
sys.path.insert(0, str(SRC))
|
| 16 |
|
| 17 |
try:
|
| 18 |
+
import spaces
|
| 19 |
+
except Exception:
|
| 20 |
spaces = None
|
| 21 |
|
| 22 |
from nexus_visual_weaver.catalog import catalog_summary
|
|
|
|
| 25 |
from nexus_visual_weaver.model_relay import WeaverModelRelay
|
| 26 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 27 |
from nexus_visual_weaver.provider_runtime import judge_with_minicpm, judge_with_nemotron
|
| 28 |
+
from nexus_visual_weaver.render import render_catalog_table, render_dashboard_regions
|
| 29 |
from nexus_visual_weaver.security import scan_file
|
| 30 |
from nexus_visual_weaver.styles import APP_CSS
|
| 31 |
|
|
|
|
|
|
|
| 32 |
APP_THEME = gr.themes.Soft()
|
| 33 |
|
| 34 |
DEFAULT_PROMPT = (
|
|
|
|
| 50 |
"Portrait": (832, 1216),
|
| 51 |
}
|
| 52 |
|
|
|
|
| 53 |
def _default_operator_state() -> dict[str, Any]:
|
| 54 |
return {
|
| 55 |
"provider_state": "idle",
|
|
|
|
| 58 |
"message": "No operator action yet.",
|
| 59 |
}
|
| 60 |
|
|
|
|
| 61 |
def _zero_gpu_entrypoint(fn: Any) -> Any:
|
| 62 |
gpu_decorator = getattr(spaces, "GPU", None) if spaces is not None else None
|
| 63 |
if gpu_decorator is None:
|
| 64 |
return fn
|
| 65 |
return gpu_decorator(duration=300)(fn)
|
| 66 |
|
|
|
|
| 67 |
def _relay_snapshot(adult_mode: bool = False) -> dict[str, Any]:
|
| 68 |
return MODEL_RELAY.dashboard_snapshot(public_demo=not adult_mode)
|
| 69 |
|
|
|
|
| 70 |
def _file_path(uploaded: Any) -> str | None:
|
| 71 |
if uploaded is None:
|
| 72 |
return None
|
|
|
|
| 75 |
path = getattr(uploaded, "name", None)
|
| 76 |
return str(path) if path else None
|
| 77 |
|
|
|
|
| 78 |
def _safe_file_hash(path: str | None) -> tuple[str | None, int | None]:
|
| 79 |
if not path:
|
| 80 |
return None, None
|
|
|
|
| 90 |
return None, None
|
| 91 |
return sha256.hexdigest(), size
|
| 92 |
|
|
|
|
| 93 |
def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] | None:
|
| 94 |
if not reference_url:
|
| 95 |
return None
|
|
|
|
| 105 |
"message": "URL stored as metadata only; Space runtime does not crawl or copy shop images.",
|
| 106 |
}
|
| 107 |
|
|
|
|
| 108 |
def _reference_metadata(uploaded: Any, reference_url: str | None, scan: dict[str, Any]) -> list[dict[str, Any]]:
|
| 109 |
records: list[dict[str, Any]] = []
|
| 110 |
path = _file_path(uploaded)
|
| 111 |
if path:
|
| 112 |
file_hash, size = _safe_file_hash(path)
|
| 113 |
+
records.append({
|
| 114 |
+
"source": "upload",
|
| 115 |
+
"basename": Path(path).name,
|
| 116 |
+
"sha256": file_hash,
|
| 117 |
+
"size_bytes": size,
|
| 118 |
+
"st3gg_status": scan.get("status"),
|
| 119 |
+
"export_gate": scan.get("export_gate"),
|
| 120 |
+
"magic": scan.get("magic"),
|
| 121 |
+
"extension": scan.get("extension"),
|
| 122 |
+
})
|
|
|
|
|
|
|
| 123 |
url_record = _safe_reference_url_metadata(reference_url)
|
| 124 |
if url_record:
|
| 125 |
records.append(url_record)
|
| 126 |
return records
|
| 127 |
|
|
|
|
| 128 |
def _creator_controls(
|
| 129 |
reasoning_mode: str,
|
| 130 |
video_preset: str,
|
|
|
|
| 163 |
},
|
| 164 |
}
|
| 165 |
|
|
|
|
| 166 |
def _resolve_seed(seed_value: Any) -> int:
|
| 167 |
try:
|
| 168 |
if seed_value is None or str(seed_value).strip() == "":
|
|
|
|
| 172 |
return secrets.randbelow(1_000_000_000)
|
| 173 |
return secrets.randbelow(1_000_000_000) if seed < 0 else seed
|
| 174 |
|
|
|
|
| 175 |
def _generation_dimensions(aspect: str | None) -> tuple[int, int]:
|
| 176 |
return ASPECT_DIMENSIONS.get(str(aspect or "Portrait"), ASPECT_DIMENSIONS["Portrait"])
|
| 177 |
|
|
|
|
| 178 |
def _style_modifier(style_strength: str | None) -> str:
|
| 179 |
return STYLE_MODIFIERS.get(str(style_strength or "High Fashion"), STYLE_MODIFIERS["High Fashion"])
|
| 180 |
|
|
|
|
| 181 |
def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
| 182 |
wardrobe = controls.get("wardrobe", {})
|
| 183 |
additions = [
|
|
|
|
| 196 |
prompt = f"{prompt}\nWardrobe controls: {suffix}" if suffix else prompt
|
| 197 |
return f"{prompt}\nStyle direction: {style}"
|
| 198 |
|
|
|
|
| 199 |
def _generated_output_path(operator_state: dict[str, Any] | None) -> str | None:
|
| 200 |
generation = (operator_state or {}).get("generation") or {}
|
| 201 |
output_path = generation.get("output_path")
|
| 202 |
return str(output_path) if output_path else None
|
| 203 |
|
|
|
|
| 204 |
def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict[str, Any]:
|
| 205 |
output_path = _generated_output_path(operator_state)
|
| 206 |
if output_path:
|
|
|
|
| 208 |
stored_scan = (operator_state or {}).get("generated_scan")
|
| 209 |
return stored_scan if isinstance(stored_scan, dict) else scan_file(None)
|
| 210 |
|
|
|
|
| 211 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
| 212 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 213 |
if not suffix:
|
|
|
|
| 217 |
except ValueError:
|
| 218 |
return 0
|
| 219 |
|
|
|
|
| 220 |
def _wardrobe_summary(run: Any) -> str:
|
| 221 |
slots = getattr(getattr(run, "outfit", None), "slots", []) or []
|
| 222 |
return "; ".join(
|
|
|
|
| 224 |
for slot in slots
|
| 225 |
)
|
| 226 |
|
|
|
|
| 227 |
SECTIONS = ["Forge", "Wardrobe", "Lore", "Models", "Security", "Runs"]
|
| 228 |
|
|
|
|
| 229 |
def _button_updates(run: Any | None, operator_state: dict[str, Any] | None) -> tuple[Any, Any, Any]:
|
| 230 |
state = operator_state or {}
|
| 231 |
generated = bool(_generated_output_path(state)) and (state.get("generation") or {}).get("status") == "success"
|
|
|
|
| 237 |
gr.update(interactive=False),
|
| 238 |
)
|
| 239 |
|
|
|
|
| 240 |
def _dashboard_regions(
|
| 241 |
run: Any | None = None,
|
| 242 |
adult_mode: bool = False,
|
|
|
|
| 253 |
operator_state=operator_state,
|
| 254 |
)
|
| 255 |
|
|
|
|
| 256 |
# βββ Modal Integration βββ
|
| 257 |
MODAL_AVAILABLE = False
|
| 258 |
try:
|
|
|
|
| 261 |
except ImportError:
|
| 262 |
pass
|
| 263 |
|
|
|
|
| 264 |
LORA_ADAPTERS = {
|
| 265 |
"garment": {"repo": "NO8D/BodyControl", "desc": "Body/garment shape control", "weight": 0.75},
|
| 266 |
"hardware": {"repo": "NO8D/ExpressionControl", "desc": "Expression/hardware detail", "weight": 0.70},
|
|
|
|
| 279 |
|
| 280 |
MODAL_COST_TRACKER = {"credits_remaining": 250.88, "total_spent": 0.0, "refinements": 0}
|
| 281 |
|
|
|
|
| 282 |
def _modal_refine_image(image_bytes: bytes, user_addition: str, gpu_type: str = "A100-80GB",
|
| 283 |
strength: float = 0.58, steps: int = 32, guidance_scale: float = 3.8,
|
| 284 |
seed: int = -1, lora_adapters: list | None = None,
|
| 285 |
negative_prompt: str = "blurry, low quality, deformed, extra limbs") -> tuple:
|
|
|
|
| 286 |
if not MODAL_AVAILABLE:
|
| 287 |
+
return None, "β Modal not installed"
|
|
|
|
| 288 |
try:
|
| 289 |
fn = modal.Function.lookup("nexus-couture-refine-v2", "refine_couture")
|
| 290 |
result_bytes = fn.remote(
|
|
|
|
| 298 |
negative_prompt=negative_prompt,
|
| 299 |
gpu_type=gpu_type,
|
| 300 |
)
|
|
|
|
| 301 |
gpu_info = GPU_OPTIONS.get(gpu_type, GPU_OPTIONS["A100-80GB"])
|
| 302 |
est_cost = round(gpu_info["price"] * (steps / 60), 4)
|
| 303 |
MODAL_COST_TRACKER["total_spent"] += est_cost
|
| 304 |
MODAL_COST_TRACKER["credits_remaining"] -= est_cost
|
| 305 |
MODAL_COST_TRACKER["refinements"] += 1
|
| 306 |
+
return result_bytes, f"β
Modal refinement complete on {gpu_type}"
|
| 307 |
except Exception as e:
|
| 308 |
return None, f"β Modal error: {str(e)[:200]}"
|
| 309 |
|
|
|
|
| 310 |
def _modal_health_check() -> dict:
|
|
|
|
| 311 |
if not MODAL_AVAILABLE:
|
| 312 |
+
return {"status": "unavailable", "message": "Modal not installed"}
|
| 313 |
try:
|
| 314 |
fn = modal.Function.lookup("nexus-couture-refine-v2", "check_modal_health")
|
| 315 |
return fn.remote()
|
| 316 |
except Exception as e:
|
| 317 |
return {"status": "error", "message": str(e)[:200]}
|
| 318 |
|
|
|
|
| 319 |
@_zero_gpu_entrypoint
|
| 320 |
def run_weave(
|
| 321 |
prompt, reasoning_mode, video_preset, adult_mode, upload, active_section,
|
|
|
|
| 355 |
provider_state = generation.provider_state
|
| 356 |
else:
|
| 357 |
provider_state = "checkpointed"
|
| 358 |
+
|
| 359 |
operator_state = {
|
| 360 |
"provider_state": provider_state,
|
| 361 |
"checkpoint": "pending_review",
|
|
|
|
| 383 |
*_button_updates(run, operator_state),
|
| 384 |
)
|
| 385 |
|
|
|
|
| 386 |
def toggle_adult_visibility(adult_mode, active_section, upload):
|
| 387 |
scan = scan_file(_file_path(upload))
|
| 388 |
operator_state = {
|
|
|
|
| 396 |
render_catalog_table(adult_mode=adult_mode), catalog_summary(adult_mode), scan, operator_state,
|
| 397 |
)
|
| 398 |
|
|
|
|
| 399 |
def refresh_section(active_section, adult_mode, run, scan, operator_state):
|
| 400 |
scan = scan or scan_file(None)
|
| 401 |
regions = _dashboard_regions(
|
|
|
|
| 404 |
)
|
| 405 |
return regions["command_rail"], regions["operations"], regions["inspector"], regions["artifacts"], regions["providers"], scan
|
| 406 |
|
|
|
|
| 407 |
def _render_stateful(run, adult_mode, scan, active_section, operator_state):
|
| 408 |
scan = scan or scan_file(None)
|
| 409 |
regions = _dashboard_regions(
|
|
|
|
| 420 |
*_button_updates(run, operator_state),
|
| 421 |
)
|
| 422 |
|
|
|
|
| 423 |
def scan_reference(run, adult_mode, upload, active_section, operator_state, reference_url=None):
|
| 424 |
state = operator_state or _default_operator_state()
|
| 425 |
reference_path = _file_path(upload)
|
|
|
|
| 448 |
rendered = _render_stateful(run, adult_mode, generated_scan, active_section, next_state)
|
| 449 |
return (*rendered, generated_scan)
|
| 450 |
|
|
|
|
| 451 |
def approve_checkpoint(run, adult_mode, scan, active_section, operator_state):
|
| 452 |
state = operator_state or _default_operator_state()
|
| 453 |
scan = _authoritative_generated_scan(state)
|
|
|
|
| 469 |
}
|
| 470 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 471 |
|
|
|
|
| 472 |
def export_packet(run, adult_mode, scan, active_section, operator_state, override_reason=None):
|
| 473 |
state = operator_state or _default_operator_state()
|
| 474 |
scan = _authoritative_generated_scan(state)
|
|
|
|
| 493 |
}
|
| 494 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 495 |
|
|
|
|
| 496 |
def stop_provider_job(run, adult_mode, scan, active_section, operator_state):
|
| 497 |
scan = scan or scan_file(None)
|
| 498 |
next_state = {
|
|
|
|
| 502 |
}
|
| 503 |
return _render_stateful(run, adult_mode, scan, active_section, next_state)
|
| 504 |
|
|
|
|
| 505 |
def reset_demo(adult_mode, active_section):
|
| 506 |
scan = scan_file(None)
|
| 507 |
operator_state = _default_operator_state()
|
|
|
|
| 515 |
gr.update(interactive=False), gr.update(interactive=False), gr.update(interactive=False),
|
| 516 |
)
|
| 517 |
|
|
|
|
| 518 |
# βββ Modal Tab Handlers βββ
|
|
|
|
| 519 |
def modal_refine_handler(input_image, user_addition, gpu_type, strength, steps, guidance, seed, lora_choices, negative_prompt):
|
|
|
|
| 520 |
if input_image is None:
|
| 521 |
return None, "β No input image provided"
|
| 522 |
from PIL import Image as PILImage
|
|
|
|
| 528 |
img = PILImage.open(input_image)
|
| 529 |
img.save(buf, format="PNG")
|
| 530 |
image_bytes = buf.getvalue()
|
|
|
|
| 531 |
result_bytes, message = _modal_refine_image(
|
| 532 |
image_bytes=image_bytes, user_addition=user_addition,
|
| 533 |
gpu_type=gpu_type, strength=strength, steps=int(steps),
|
|
|
|
| 541 |
return result_img, f"{message}\n{cost_info}"
|
| 542 |
return None, message
|
| 543 |
|
|
|
|
| 544 |
def modal_health_handler():
|
|
|
|
| 545 |
result = _modal_health_check()
|
| 546 |
if result.get("status") == "healthy":
|
| 547 |
+
return f"β
Modal connected\nGPU: {result.get('gpu', 'N/A')}"
|
| 548 |
elif result.get("status") == "unavailable":
|
| 549 |
return f"β οΈ {result.get('message', 'Modal not available')}"
|
| 550 |
else:
|
| 551 |
return f"β Modal error: {result.get('message', 'Unknown')}"
|
| 552 |
|
|
|
|
| 553 |
initial_operator_state = _default_operator_state()
|
| 554 |
initial_regions = _dashboard_regions(scan=scan_file(None), operator_state=initial_operator_state)
|
| 555 |
|
|
|
|
| 560 |
topbar_html = gr.HTML(initial_regions["topbar"], container=False, visible=False)
|
| 561 |
|
| 562 |
with gr.Tabs():
|
| 563 |
+
# βββ Tab 1: Studio βββ
|
| 564 |
with gr.Tab("π§΅ Studio"):
|
| 565 |
with gr.Row(elem_id="nw-creator-workbench", elem_classes=["nw-creator-workbench"]):
|
| 566 |
with gr.Column(scale=5, min_width=520, elem_id="nw-creator-panel"):
|
|
|
|
| 595 |
with gr.Row():
|
| 596 |
scan_btn = gr.Button("Scan Uploaded File", scale=1)
|
| 597 |
stop_btn = gr.Button("Stop Job", variant="stop", interactive=False, scale=1)
|
|
|
|
| 598 |
with gr.Column(scale=4, min_width=460, elem_id="nw-output-panel"):
|
| 599 |
gr.Markdown("### Output")
|
| 600 |
artifact_html = gr.HTML(initial_regions["artifacts"], container=False)
|
|
|
|
| 608 |
)
|
| 609 |
gr.Markdown("Generation is not export. Every artifact stays behind ST3GG review and human checkpoint.")
|
| 610 |
|
| 611 |
+
# βββ Tab 2: Modal Refinement (ACTIVE) βββ
|
| 612 |
with gr.Tab("β‘ Modal"):
|
| 613 |
gr.Markdown("## β‘ Modal GPU Refinement")
|
| 614 |
gr.Markdown("Send a generated image to Modal for FLUX.1-Kontext-dev refinement with multi-LoRA on dedicated GPU.")
|
|
|
|
| 644 |
)
|
| 645 |
modal_health_btn.click(fn=modal_health_handler, inputs=[], outputs=[modal_status])
|
| 646 |
|
| 647 |
+
# βββ Tab 3: LoRA Lab (ACTIVE) βββ
|
| 648 |
with gr.Tab("π§ͺ LoRA Lab"):
|
| 649 |
gr.Markdown("## π§ͺ LoRA Training Lab")
|
| 650 |
gr.Markdown("Train custom LoRA adapters on Modal GPU. Connect a dataset repo and configure training parameters.")
|
|
|
|
| 669 |
for k, v in LORA_ADAPTERS.items()
|
| 670 |
)
|
| 671 |
gr.Markdown(lora_catalog_md)
|
|
|
|
| 672 |
def lora_train_handler(dataset_repo, output_name, rank, lr, steps, batch, push, hub_repo):
|
| 673 |
if not MODAL_AVAILABLE:
|
| 674 |
return "β Modal not installed"
|
|
|
|
| 682 |
return f"β
Training triggered on Modal!\nDataset: {dataset_repo}\nOutput: {output_name}\nRank: {rank}, Steps: {steps}, LR: {lr}"
|
| 683 |
except Exception as e:
|
| 684 |
return f"β Training error: {str(e)[:300]}"
|
|
|
|
| 685 |
lora_train_btn.click(
|
| 686 |
fn=lora_train_handler,
|
| 687 |
inputs=[lora_dataset_repo, lora_output_name, lora_rank, lora_lr,
|
|
|
|
| 729 |
outputs=stateful_outputs, api_name="run_active_weave",
|
| 730 |
concurrency_limit=1, concurrency_id="flux-gpu",
|
| 731 |
)
|
| 732 |
+
|
| 733 |
run_submit = prompt.submit(
|
| 734 |
fn=run_weave,
|
| 735 |
inputs=[prompt, reasoning_mode, video_preset, adult_mode, upload, section_nav,
|
|
|
|
| 738 |
outputs=stateful_outputs, api_name=False,
|
| 739 |
concurrency_limit=1, concurrency_id="flux-gpu",
|
| 740 |
)
|
| 741 |
+
|
| 742 |
adult_mode.change(
|
| 743 |
fn=toggle_adult_visibility,
|
| 744 |
inputs=[adult_mode, section_nav, upload],
|
|
|
|
| 746 |
artifact_html, provider_html, catalog_html, catalog_json, scan_json, operator_state],
|
| 747 |
api_name="toggle_adult_catalog", queue=False,
|
| 748 |
)
|
| 749 |
+
|
| 750 |
section_nav.change(
|
| 751 |
fn=refresh_section,
|
| 752 |
inputs=[section_nav, adult_mode, active_run_state, scan_state, operator_state],
|
| 753 |
outputs=[command_rail_html, operations_html, inspector_html, artifact_html, provider_html, scan_json],
|
| 754 |
api_name=False, queue=False,
|
| 755 |
)
|
| 756 |
+
|
| 757 |
scan_btn.click(
|
| 758 |
fn=scan_reference,
|
| 759 |
inputs=[active_run_state, adult_mode, upload, section_nav, operator_state, reference_url],
|
| 760 |
outputs=dashboard_outputs + [operator_state, checkpoint_btn, export_btn, stop_btn, scan_state],
|
| 761 |
api_name="scan_reference", queue=False,
|
| 762 |
)
|
| 763 |
+
|
| 764 |
checkpoint_btn.click(
|
| 765 |
fn=approve_checkpoint,
|
| 766 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state],
|
| 767 |
outputs=operator_outputs, api_name="approve_checkpoint", queue=False,
|
| 768 |
)
|
| 769 |
+
|
| 770 |
export_btn.click(
|
| 771 |
fn=export_packet,
|
| 772 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state, override_reason],
|
| 773 |
outputs=operator_outputs, api_name="prepare_export_packet", queue=False,
|
| 774 |
)
|
| 775 |
+
|
| 776 |
stop_btn.click(
|
| 777 |
fn=stop_provider_job,
|
| 778 |
inputs=[active_run_state, adult_mode, scan_state, section_nav, operator_state],
|
| 779 |
outputs=operator_outputs, api_name="stop_provider_job", queue=False,
|
| 780 |
cancels=[run_click, run_submit],
|
| 781 |
)
|
| 782 |
+
|
| 783 |
reset_btn.click(
|
| 784 |
fn=reset_demo,
|
| 785 |
inputs=[adult_mode, section_nav],
|
| 786 |
outputs=stateful_outputs, api_name="reset_demo_state", queue=False,
|
| 787 |
cancels=[run_click, run_submit],
|
| 788 |
)
|
| 789 |
+
|
| 790 |
demo.load(
|
| 791 |
fn=lambda: (render_catalog_table(False), catalog_summary(False), scan_file(None), scan_file(None), _default_operator_state()),
|
| 792 |
outputs=[catalog_html, catalog_json, scan_json, scan_state, operator_state],
|
| 793 |
api_name=False,
|
| 794 |
)
|
| 795 |
|
|
|
|
| 796 |
if __name__ == "__main__":
|
| 797 |
if hasattr(sys.stdout, "reconfigure"):
|
| 798 |
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
|
|
|
| 801 |
demo.launch(
|
| 802 |
server_name="0.0.0.0",
|
| 803 |
server_port=int(os.environ.get("NEXUS_PORT", os.environ.get("PORT", "7860"))),
|
| 804 |
+
quiet=True,
|
| 805 |
+
mcp_server=True,
|
| 806 |
+
ssr_mode=False,
|
| 807 |
css=APP_CSS,
|
| 808 |
+
)
|