fix pr7 review findings
Browse files- app.py +195 -6
- src/nexus_visual_weaver/catalog.py +37 -0
- src/nexus_visual_weaver/exporter.py +82 -2
- src/nexus_visual_weaver/hf_runtime.py +72 -0
- src/nexus_visual_weaver/lora_adapter.py +45 -0
- src/nexus_visual_weaver/model_relay.py +14 -0
- src/nexus_visual_weaver/planner.py +14 -0
- src/nexus_visual_weaver/provider_runtime.py +81 -0
- src/nexus_visual_weaver/render.py +143 -17
- src/nexus_visual_weaver/wardrobe.py +11 -0
- tests/test_app_callbacks.py +17 -0
- tests/test_command_center.py +11 -0
- tests/test_exporter.py +34 -0
- tests/test_lora_adapter.py +6 -0
app.py
CHANGED
|
@@ -59,7 +59,17 @@ def _default_operator_state() -> dict[str, Any]:
|
|
| 59 |
|
| 60 |
|
| 61 |
def _zero_gpu_entrypoint(fn: Any) -> Any:
|
| 62 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
gpu_decorator = getattr(spaces, "GPU", None) if spaces is not None else None
|
| 64 |
if gpu_decorator is None:
|
| 65 |
return fn
|
|
@@ -67,10 +77,22 @@ def _zero_gpu_entrypoint(fn: Any) -> Any:
|
|
| 67 |
|
| 68 |
|
| 69 |
def _relay_snapshot(adult_mode: bool = False) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
return MODEL_RELAY.dashboard_snapshot(public_demo=not adult_mode)
|
| 71 |
|
| 72 |
|
| 73 |
def _file_path(uploaded: Any) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
if uploaded is None:
|
| 75 |
return None
|
| 76 |
if isinstance(uploaded, str):
|
|
@@ -80,17 +102,36 @@ def _file_path(uploaded: Any) -> str | None:
|
|
| 80 |
|
| 81 |
|
| 82 |
def _safe_file_hash(path: str | None) -> tuple[str | None, int | None]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
if not path:
|
| 84 |
return None, None
|
| 85 |
try:
|
| 86 |
target = Path(path)
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
except OSError:
|
| 89 |
return None, None
|
| 90 |
-
return
|
| 91 |
|
| 92 |
|
| 93 |
def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 94 |
if not reference_url:
|
| 95 |
return None
|
| 96 |
parsed = urlparse(reference_url.strip())
|
|
@@ -107,6 +148,20 @@ def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] |
|
|
| 107 |
|
| 108 |
|
| 109 |
def _reference_metadata(uploaded: Any, reference_url: str | None, scan: dict[str, Any]) -> list[dict[str, Any]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
records: list[dict[str, Any]] = []
|
| 111 |
path = _file_path(uploaded)
|
| 112 |
if path:
|
|
@@ -140,6 +195,13 @@ def _creator_controls(
|
|
| 140 |
hardware: str | None = None,
|
| 141 |
locate_focus: list[str] | None = None,
|
| 142 |
) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
wardrobe = {
|
| 144 |
"silhouette": silhouette or "structured long coat",
|
| 145 |
"outerwear": outerwear or "black patent leather long coat",
|
|
@@ -163,6 +225,18 @@ def _creator_controls(
|
|
| 163 |
|
| 164 |
|
| 165 |
def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
wardrobe = controls.get("wardrobe", {})
|
| 167 |
additions = [
|
| 168 |
wardrobe.get("silhouette"),
|
|
@@ -177,12 +251,24 @@ def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
|
| 177 |
|
| 178 |
|
| 179 |
def _generated_output_path(operator_state: dict[str, Any] | None) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 180 |
generation = (operator_state or {}).get("generation") or {}
|
| 181 |
output_path = generation.get("output_path")
|
| 182 |
return str(output_path) if output_path else None
|
| 183 |
|
| 184 |
|
| 185 |
def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
output_path = _generated_output_path(operator_state)
|
| 187 |
if output_path:
|
| 188 |
return scan_file(output_path)
|
|
@@ -191,6 +277,15 @@ def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict
|
|
| 191 |
|
| 192 |
|
| 193 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 195 |
if not suffix:
|
| 196 |
return 0
|
|
@@ -201,6 +296,12 @@ def _checkpoint_seed(checkpoint_id: str) -> int:
|
|
| 201 |
|
| 202 |
|
| 203 |
def _wardrobe_summary(run: Any) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 204 |
slots = getattr(getattr(run, "outfit", None), "slots", []) or []
|
| 205 |
return "; ".join(
|
| 206 |
f"{slot.name}: {slot.description}, material={slot.material}, palette={slot.palette}, locked={slot.locked}"
|
|
@@ -244,6 +345,14 @@ def run_weave(
|
|
| 244 |
hardware: str | None = None,
|
| 245 |
reference_url: str | None = None,
|
| 246 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
prompt = prompt.strip() or DEFAULT_PROMPT
|
| 248 |
controls = _creator_controls(
|
| 249 |
reasoning_mode=reasoning_mode,
|
|
@@ -333,6 +442,14 @@ def toggle_adult_visibility(
|
|
| 333 |
active_section: str,
|
| 334 |
upload: Any,
|
| 335 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
scan = scan_file(_file_path(upload))
|
| 337 |
operator_state = {
|
| 338 |
**_default_operator_state(),
|
|
@@ -361,10 +478,12 @@ def refresh_section(
|
|
| 361 |
operator_state: dict[str, Any] | None,
|
| 362 |
) -> tuple[str, str, str, str, str, dict[str, Any]]:
|
| 363 |
"""
|
| 364 |
-
|
| 365 |
-
|
| 366 |
Returns:
|
| 367 |
-
A tuple
|
|
|
|
|
|
|
| 368 |
"""
|
| 369 |
scan = scan or scan_file(None)
|
| 370 |
regions = _dashboard_regions(
|
|
@@ -384,6 +503,25 @@ def _render_stateful(
|
|
| 384 |
active_section: str,
|
| 385 |
operator_state: dict[str, Any],
|
| 386 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
scan = scan or scan_file(None)
|
| 388 |
regions = _dashboard_regions(
|
| 389 |
run=run,
|
|
@@ -419,6 +557,16 @@ def scan_reference(
|
|
| 419 |
operator_state: dict[str, Any] | None,
|
| 420 |
reference_url: str | None = None,
|
| 421 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 422 |
state = operator_state or _default_operator_state()
|
| 423 |
reference_path = _file_path(upload)
|
| 424 |
reference_scan = scan_file(reference_path)
|
|
@@ -456,6 +604,12 @@ def approve_checkpoint(
|
|
| 456 |
active_section: str,
|
| 457 |
operator_state: dict[str, Any] | None,
|
| 458 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 459 |
state = operator_state or _default_operator_state()
|
| 460 |
scan = _authoritative_generated_scan(state)
|
| 461 |
if run is None:
|
|
@@ -488,6 +642,26 @@ def export_packet(
|
|
| 488 |
operator_state: dict[str, Any] | None,
|
| 489 |
override_reason: str | None = None,
|
| 490 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 491 |
state = operator_state or _default_operator_state()
|
| 492 |
scan = _authoritative_generated_scan(state)
|
| 493 |
override_reason = (override_reason or "").strip()
|
|
@@ -524,6 +698,15 @@ def stop_provider_job(
|
|
| 524 |
active_section: str,
|
| 525 |
operator_state: dict[str, Any] | None,
|
| 526 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
scan = scan or scan_file(None)
|
| 528 |
next_state = {
|
| 529 |
**(operator_state or _default_operator_state()),
|
|
@@ -537,6 +720,12 @@ def reset_demo(
|
|
| 537 |
adult_mode: bool,
|
| 538 |
active_section: str,
|
| 539 |
) -> tuple[Any, ...]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 540 |
scan = scan_file(None)
|
| 541 |
operator_state = _default_operator_state()
|
| 542 |
regions = _dashboard_regions(adult_mode=adult_mode, scan=scan, active_section=active_section, operator_state=operator_state)
|
|
|
|
| 59 |
|
| 60 |
|
| 61 |
def _zero_gpu_entrypoint(fn: Any) -> Any:
|
| 62 |
+
"""
|
| 63 |
+
Optionally wrap a function with ZeroGPU acceleration.
|
| 64 |
+
|
| 65 |
+
If the spaces module is available and provides GPU support, wraps the function with `spaces.GPU(duration=300)`. Otherwise, returns the function unchanged.
|
| 66 |
+
|
| 67 |
+
Parameters:
|
| 68 |
+
fn: The callback function.
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
The function, optionally wrapped with ZeroGPU acceleration.
|
| 72 |
+
"""
|
| 73 |
gpu_decorator = getattr(spaces, "GPU", None) if spaces is not None else None
|
| 74 |
if gpu_decorator is None:
|
| 75 |
return fn
|
|
|
|
| 77 |
|
| 78 |
|
| 79 |
def _relay_snapshot(adult_mode: bool = False) -> dict[str, Any]:
|
| 80 |
+
"""
|
| 81 |
+
Retrieves the relay dashboard snapshot based on visibility mode.
|
| 82 |
+
|
| 83 |
+
Returns:
|
| 84 |
+
dict[str, Any]: Dashboard snapshot containing relay status and model information.
|
| 85 |
+
"""
|
| 86 |
return MODEL_RELAY.dashboard_snapshot(public_demo=not adult_mode)
|
| 87 |
|
| 88 |
|
| 89 |
def _file_path(uploaded: Any) -> str | None:
|
| 90 |
+
"""
|
| 91 |
+
Extract a file path from various upload input formats.
|
| 92 |
+
|
| 93 |
+
Returns:
|
| 94 |
+
str | None: The file path string, or None if the input is None or lacks a valid path.
|
| 95 |
+
"""
|
| 96 |
if uploaded is None:
|
| 97 |
return None
|
| 98 |
if isinstance(uploaded, str):
|
|
|
|
| 102 |
|
| 103 |
|
| 104 |
def _safe_file_hash(path: str | None) -> tuple[str | None, int | None]:
|
| 105 |
+
"""
|
| 106 |
+
Compute the SHA-256 hash and size of a file.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
tuple[str | None, int | None]: The file's SHA-256 hash as a hex string and size in bytes. Returns (None, None) if the path is falsy or the file cannot be read.
|
| 110 |
+
"""
|
| 111 |
if not path:
|
| 112 |
return None, None
|
| 113 |
try:
|
| 114 |
target = Path(path)
|
| 115 |
+
sha256 = hashlib.sha256()
|
| 116 |
+
size = 0
|
| 117 |
+
with target.open("rb") as handle:
|
| 118 |
+
while chunk := handle.read(1024 * 1024):
|
| 119 |
+
sha256.update(chunk)
|
| 120 |
+
size += len(chunk)
|
| 121 |
except OSError:
|
| 122 |
return None, None
|
| 123 |
+
return sha256.hexdigest(), size
|
| 124 |
|
| 125 |
|
| 126 |
def _safe_reference_url_metadata(reference_url: str | None) -> dict[str, Any] | None:
|
| 127 |
+
"""
|
| 128 |
+
Validates a reference URL and extracts its metadata.
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
A dict with status "metadata_only" containing domain (lowercased) and URL hash if valid;
|
| 132 |
+
a dict with status "invalid_url" if the URL scheme is not HTTP(S) or domain is missing;
|
| 133 |
+
None if reference_url is falsy.
|
| 134 |
+
"""
|
| 135 |
if not reference_url:
|
| 136 |
return None
|
| 137 |
parsed = urlparse(reference_url.strip())
|
|
|
|
| 148 |
|
| 149 |
|
| 150 |
def _reference_metadata(uploaded: Any, reference_url: str | None, scan: dict[str, Any]) -> list[dict[str, Any]]:
|
| 151 |
+
"""
|
| 152 |
+
Builds a list of metadata records from an uploaded file and/or reference URL.
|
| 153 |
+
|
| 154 |
+
For an uploaded file, includes basename, SHA-256 hash, size, and scan results (status,
|
| 155 |
+
export gate, magic, extension). For a reference URL, includes domain, URL hash, and status.
|
| 156 |
+
|
| 157 |
+
Parameters:
|
| 158 |
+
uploaded: An uploaded file object, path string, or None.
|
| 159 |
+
reference_url: Optional reference URL string.
|
| 160 |
+
scan: Dictionary of ST3GG scan results for the uploaded file.
|
| 161 |
+
|
| 162 |
+
Returns:
|
| 163 |
+
List of metadata dictionaries for the file and/or URL. Empty if neither is provided.
|
| 164 |
+
"""
|
| 165 |
records: list[dict[str, Any]] = []
|
| 166 |
path = _file_path(uploaded)
|
| 167 |
if path:
|
|
|
|
| 195 |
hardware: str | None = None,
|
| 196 |
locate_focus: list[str] | None = None,
|
| 197 |
) -> dict[str, Any]:
|
| 198 |
+
"""
|
| 199 |
+
Create a control object combining wardrobe selections with generation policy and reasoning configuration.
|
| 200 |
+
|
| 201 |
+
Returns:
|
| 202 |
+
dict: Nested structure containing reasoning mode, video preset, wardrobe selections with locked slots,
|
| 203 |
+
and FLUX generation configuration.
|
| 204 |
+
"""
|
| 205 |
wardrobe = {
|
| 206 |
"silhouette": silhouette or "structured long coat",
|
| 207 |
"outerwear": outerwear or "black patent leather long coat",
|
|
|
|
| 225 |
|
| 226 |
|
| 227 |
def _prompt_with_controls(prompt: str, controls: dict[str, Any]) -> str:
|
| 228 |
+
"""
|
| 229 |
+
Augments a prompt with wardrobe control parameters.
|
| 230 |
+
|
| 231 |
+
If any wardrobe fields are specified in the controls, appends them to the
|
| 232 |
+
prompt with a "Wardrobe controls:" prefix. Otherwise returns the prompt unchanged.
|
| 233 |
+
|
| 234 |
+
Parameters:
|
| 235 |
+
controls (dict[str, Any]): A controls dictionary containing a "wardrobe" key with fields for silhouette, outerwear, upper_body, footwear, palette, and hardware.
|
| 236 |
+
|
| 237 |
+
Returns:
|
| 238 |
+
str: The prompt with wardrobe controls appended, or the original prompt if no wardrobe items are specified.
|
| 239 |
+
"""
|
| 240 |
wardrobe = controls.get("wardrobe", {})
|
| 241 |
additions = [
|
| 242 |
wardrobe.get("silhouette"),
|
|
|
|
| 251 |
|
| 252 |
|
| 253 |
def _generated_output_path(operator_state: dict[str, Any] | None) -> str | None:
|
| 254 |
+
"""
|
| 255 |
+
Extract the generated artifact output path from the operator state.
|
| 256 |
+
|
| 257 |
+
Returns:
|
| 258 |
+
The output path string if a generated artifact exists, None otherwise.
|
| 259 |
+
"""
|
| 260 |
generation = (operator_state or {}).get("generation") or {}
|
| 261 |
output_path = generation.get("output_path")
|
| 262 |
return str(output_path) if output_path else None
|
| 263 |
|
| 264 |
|
| 265 |
def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict[str, Any]:
|
| 266 |
+
"""
|
| 267 |
+
Obtain the current scan for a generated artifact.
|
| 268 |
+
|
| 269 |
+
Returns:
|
| 270 |
+
dict[str, Any]: A scan record sourced from the generated output file, stored state, or a default scan.
|
| 271 |
+
"""
|
| 272 |
output_path = _generated_output_path(operator_state)
|
| 273 |
if output_path:
|
| 274 |
return scan_file(output_path)
|
|
|
|
| 277 |
|
| 278 |
|
| 279 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
| 280 |
+
"""
|
| 281 |
+
Derives a numeric seed from a checkpoint ID.
|
| 282 |
+
|
| 283 |
+
Parameters:
|
| 284 |
+
checkpoint_id (str): A checkpoint identifier string.
|
| 285 |
+
|
| 286 |
+
Returns:
|
| 287 |
+
int: An integer seed bounded to less than 1,000,000, or 0 if no valid seed data can be extracted from the checkpoint ID.
|
| 288 |
+
"""
|
| 289 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 290 |
if not suffix:
|
| 291 |
return 0
|
|
|
|
| 296 |
|
| 297 |
|
| 298 |
def _wardrobe_summary(run: Any) -> str:
|
| 299 |
+
"""
|
| 300 |
+
Formats outfit wardrobe slots into a semicolon-separated summary string.
|
| 301 |
+
|
| 302 |
+
Returns:
|
| 303 |
+
A semicolon-separated string listing each outfit slot's name, description, material, palette, and locked status.
|
| 304 |
+
"""
|
| 305 |
slots = getattr(getattr(run, "outfit", None), "slots", []) or []
|
| 306 |
return "; ".join(
|
| 307 |
f"{slot.name}: {slot.description}, material={slot.material}, palette={slot.palette}, locked={slot.locked}"
|
|
|
|
| 345 |
hardware: str | None = None,
|
| 346 |
reference_url: str | None = None,
|
| 347 |
) -> tuple[Any, ...]:
|
| 348 |
+
"""
|
| 349 |
+
Execute the complete weaving workflow from prompt through image generation and evaluation.
|
| 350 |
+
|
| 351 |
+
Assembles wardrobe controls, generates an image via FLUX, scans and judges the output through ST3GG scanning and dual judges (Minicpm and Nemotron), and compiles operator state reflecting generation status, checkpoint readiness, and export gating.
|
| 352 |
+
|
| 353 |
+
Returns:
|
| 354 |
+
Tuple containing dashboard region HTML fragments (topbar, command_rail, workflow, operations, inspector, drawer, status, artifacts, providers), catalog HTML, run data, catalog summary, scan results, operator state with generation details and judge evidence, and button state updates.
|
| 355 |
+
"""
|
| 356 |
prompt = prompt.strip() or DEFAULT_PROMPT
|
| 357 |
controls = _creator_controls(
|
| 358 |
reasoning_mode=reasoning_mode,
|
|
|
|
| 442 |
active_section: str,
|
| 443 |
upload: Any,
|
| 444 |
) -> tuple[Any, ...]:
|
| 445 |
+
"""
|
| 446 |
+
Update the dashboard to reflect a change in adult content visibility.
|
| 447 |
+
|
| 448 |
+
Re-scans any uploaded file and regenerates all dashboard regions to show or hide adult content while maintaining ST3GG, consent, and export gates.
|
| 449 |
+
|
| 450 |
+
Returns:
|
| 451 |
+
tuple: Updated UI fragments and state (topbar, command rail, operations, inspector, artifacts, providers, catalog table, catalog summary, scan metadata, operator state).
|
| 452 |
+
"""
|
| 453 |
scan = scan_file(_file_path(upload))
|
| 454 |
operator_state = {
|
| 455 |
**_default_operator_state(),
|
|
|
|
| 478 |
operator_state: dict[str, Any] | None,
|
| 479 |
) -> tuple[str, str, str, str, str, dict[str, Any]]:
|
| 480 |
"""
|
| 481 |
+
Render dashboard regions for the currently selected navigation section.
|
| 482 |
+
|
| 483 |
Returns:
|
| 484 |
+
A tuple of (command_rail, operations, inspector, artifacts, providers, scan),
|
| 485 |
+
where the first five elements are HTML strings for dashboard regions and the last
|
| 486 |
+
is the ST3GG scan results dictionary.
|
| 487 |
"""
|
| 488 |
scan = scan or scan_file(None)
|
| 489 |
regions = _dashboard_regions(
|
|
|
|
| 503 |
active_section: str,
|
| 504 |
operator_state: dict[str, Any],
|
| 505 |
) -> tuple[Any, ...]:
|
| 506 |
+
"""
|
| 507 |
+
Render the dashboard with current state and return all UI outputs and state objects.
|
| 508 |
+
|
| 509 |
+
Ensures scan data exists, calls the dashboard region renderer with current state, and assembles
|
| 510 |
+
a comprehensive tuple of HTML fragments, state objects, and button updates for Gradio output.
|
| 511 |
+
|
| 512 |
+
Parameters:
|
| 513 |
+
run: The current run object, or None if no run is active.
|
| 514 |
+
adult_mode: Whether adult-mode visibility is enabled.
|
| 515 |
+
scan: Scan/ST3GG evidence dict. If None or falsy, defaults to empty scan from scan_file.
|
| 516 |
+
active_section: The currently active dashboard section identifier.
|
| 517 |
+
operator_state: Current operator state dict containing provider status, checkpoint, export, and messaging.
|
| 518 |
+
|
| 519 |
+
Returns:
|
| 520 |
+
A tuple containing: topbar, command_rail, workflow, operations, inspector, drawer, status,
|
| 521 |
+
artifacts, providers (all HTML fragments), catalog table HTML, run dict (or empty dict),
|
| 522 |
+
catalog summary, scan dict, operator_state dict, and a Gradio update object for the stop
|
| 523 |
+
button (interactive if run exists and provider is neither idle, stopped, nor exported).
|
| 524 |
+
"""
|
| 525 |
scan = scan or scan_file(None)
|
| 526 |
regions = _dashboard_regions(
|
| 527 |
run=run,
|
|
|
|
| 557 |
operator_state: dict[str, Any] | None,
|
| 558 |
reference_url: str | None = None,
|
| 559 |
) -> tuple[Any, ...]:
|
| 560 |
+
"""
|
| 561 |
+
Scan and evaluate a reference image or URL, updating the operator state with findings.
|
| 562 |
+
|
| 563 |
+
Parameters:
|
| 564 |
+
reference_url (str | None): Optional URL for reference metadata validation.
|
| 565 |
+
Only domain and URL hash are recorded; no content crawling or copying occurs.
|
| 566 |
+
|
| 567 |
+
Returns:
|
| 568 |
+
Rendered dashboard outputs and the computed generated scan.
|
| 569 |
+
"""
|
| 570 |
state = operator_state or _default_operator_state()
|
| 571 |
reference_path = _file_path(upload)
|
| 572 |
reference_scan = scan_file(reference_path)
|
|
|
|
| 604 |
active_section: str,
|
| 605 |
operator_state: dict[str, Any] | None,
|
| 606 |
) -> tuple[Any, ...]:
|
| 607 |
+
"""
|
| 608 |
+
Approves the checkpoint if a run and generated artifact exist, blocking approval otherwise. Sets provider readiness based on the ST3GG export gate status.
|
| 609 |
+
|
| 610 |
+
Returns:
|
| 611 |
+
tuple[Any, ...]: Updated dashboard and operator state reflecting the checkpoint decision.
|
| 612 |
+
"""
|
| 613 |
state = operator_state or _default_operator_state()
|
| 614 |
scan = _authoritative_generated_scan(state)
|
| 615 |
if run is None:
|
|
|
|
| 642 |
operator_state: dict[str, Any] | None,
|
| 643 |
override_reason: str | None = None,
|
| 644 |
) -> tuple[Any, ...]:
|
| 645 |
+
"""
|
| 646 |
+
Prepare an export packet for the generated artifact with precondition validation and ST3GG gating.
|
| 647 |
+
|
| 648 |
+
Validates that a run, approved checkpoint, and generated artifact exist, and checks the
|
| 649 |
+
ST3GG export gate status. Blocks export if any precondition fails or if the gate is not
|
| 650 |
+
clear (unless an explicit override reason is provided). Writes a governed export packet
|
| 651 |
+
when the gate is clear, or an audit-marked packet when overridden.
|
| 652 |
+
|
| 653 |
+
Parameters:
|
| 654 |
+
run: Active run packet; export is blocked if None.
|
| 655 |
+
adult_mode: Whether adult content is enabled for the export.
|
| 656 |
+
scan: ST3GG scan results; checked for export gate status.
|
| 657 |
+
active_section: Current UI section for rendering.
|
| 658 |
+
operator_state: Current operator state; defaults to idle state if None.
|
| 659 |
+
override_reason: Reason to override when ST3GG gate is not clear.
|
| 660 |
+
|
| 661 |
+
Returns:
|
| 662 |
+
Tuple containing dashboard region HTML, run dict, catalog outputs, scan, operator state,
|
| 663 |
+
and stop button interactive state.
|
| 664 |
+
"""
|
| 665 |
state = operator_state or _default_operator_state()
|
| 666 |
scan = _authoritative_generated_scan(state)
|
| 667 |
override_reason = (override_reason or "").strip()
|
|
|
|
| 698 |
active_section: str,
|
| 699 |
operator_state: dict[str, Any] | None,
|
| 700 |
) -> tuple[Any, ...]:
|
| 701 |
+
"""
|
| 702 |
+
Stop the active provider job.
|
| 703 |
+
|
| 704 |
+
Halts the current image generation or provider handoff, preserving local evidence and
|
| 705 |
+
the dry-run packet. Re-renders the dashboard with provider state set to stopped.
|
| 706 |
+
|
| 707 |
+
Returns:
|
| 708 |
+
tuple[Any, ...]: Dashboard region HTML fragments, run dict, scan dict, operator state, and UI control updates.
|
| 709 |
+
"""
|
| 710 |
scan = scan or scan_file(None)
|
| 711 |
next_state = {
|
| 712 |
**(operator_state or _default_operator_state()),
|
|
|
|
| 720 |
adult_mode: bool,
|
| 721 |
active_section: str,
|
| 722 |
) -> tuple[Any, ...]:
|
| 723 |
+
"""
|
| 724 |
+
Reset the application to its initial state by clearing all generated evidence and reinitializing operator state.
|
| 725 |
+
|
| 726 |
+
Returns:
|
| 727 |
+
tuple[Any, ...]: Dashboard region HTML fragments, catalog table, empty run state, catalog summary, scan state, operator state, and button state for a reset application.
|
| 728 |
+
"""
|
| 729 |
scan = scan_file(None)
|
| 730 |
operator_state = _default_operator_state()
|
| 731 |
regions = _dashboard_regions(adult_mode=adult_mode, scan=scan, active_section=active_section, operator_state=operator_state)
|
src/nexus_visual_weaver/catalog.py
CHANGED
|
@@ -204,6 +204,15 @@ PRIVATE_RESEARCH_STACK = [
|
|
| 204 |
|
| 205 |
|
| 206 |
def filter_catalog(adult_mode: bool = False) -> tuple[list[ModelCandidate], list[AdapterRecipe]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 207 |
models = [
|
| 208 |
model
|
| 209 |
for model in MODEL_CATALOG
|
|
@@ -214,6 +223,12 @@ def filter_catalog(adult_mode: bool = False) -> tuple[list[ModelCandidate], list
|
|
| 214 |
|
| 215 |
|
| 216 |
def active_stack(adult_mode: bool = False) -> list[ModelCandidate]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 217 |
allowed, _ = filter_catalog(adult_mode)
|
| 218 |
by_id = {model.repo_id: model for model in allowed}
|
| 219 |
stack_ids = PRIVATE_RESEARCH_STACK if adult_mode else DEFAULT_ACTIVE_STACK
|
|
@@ -221,6 +236,22 @@ def active_stack(adult_mode: bool = False) -> list[ModelCandidate]:
|
|
| 221 |
|
| 222 |
|
| 223 |
def parameter_budget(stack: list[ModelCandidate] | None = None) -> dict[str, float | str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
chosen = stack or active_stack(False)
|
| 225 |
total = round(sum(model.params_b for model in chosen), 2)
|
| 226 |
return {
|
|
@@ -232,6 +263,12 @@ def parameter_budget(stack: list[ModelCandidate] | None = None) -> dict[str, flo
|
|
| 232 |
|
| 233 |
|
| 234 |
def catalog_summary(adult_mode: bool = False) -> dict[str, int | float | str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
models, adapters = filter_catalog(adult_mode)
|
| 236 |
budget = parameter_budget(active_stack(adult_mode))
|
| 237 |
return {
|
|
|
|
| 204 |
|
| 205 |
|
| 206 |
def filter_catalog(adult_mode: bool = False) -> tuple[list[ModelCandidate], list[AdapterRecipe]]:
|
| 207 |
+
"""
|
| 208 |
+
Filter the model and adapter catalogs based on visibility settings.
|
| 209 |
+
|
| 210 |
+
Parameters:
|
| 211 |
+
adult_mode (bool): When False, only public, non-adult content is included. When True, all content is included.
|
| 212 |
+
|
| 213 |
+
Returns:
|
| 214 |
+
tuple: (models, adapters) - filtered lists of ModelCandidate and AdapterRecipe objects matching the visibility criteria.
|
| 215 |
+
"""
|
| 216 |
models = [
|
| 217 |
model
|
| 218 |
for model in MODEL_CATALOG
|
|
|
|
| 223 |
|
| 224 |
|
| 225 |
def active_stack(adult_mode: bool = False) -> list[ModelCandidate]:
|
| 226 |
+
"""
|
| 227 |
+
Builds the active model stack based on access mode.
|
| 228 |
+
|
| 229 |
+
Returns:
|
| 230 |
+
list[ModelCandidate]: The active model candidates visible for the given mode.
|
| 231 |
+
"""
|
| 232 |
allowed, _ = filter_catalog(adult_mode)
|
| 233 |
by_id = {model.repo_id: model for model in allowed}
|
| 234 |
stack_ids = PRIVATE_RESEARCH_STACK if adult_mode else DEFAULT_ACTIVE_STACK
|
|
|
|
| 236 |
|
| 237 |
|
| 238 |
def parameter_budget(stack: list[ModelCandidate] | None = None) -> dict[str, float | str]:
|
| 239 |
+
"""
|
| 240 |
+
Calculates the parameter budget summary for a model stack.
|
| 241 |
+
|
| 242 |
+
Computes the total parameter count of models in the provided stack,
|
| 243 |
+
checks it against a 32 billion parameter limit, and returns budget details.
|
| 244 |
+
|
| 245 |
+
Parameters:
|
| 246 |
+
stack: Model candidates to evaluate. If None, uses the default active stack.
|
| 247 |
+
|
| 248 |
+
Returns:
|
| 249 |
+
A dictionary with the following keys:
|
| 250 |
+
- active_b (float): Total parameters in billions
|
| 251 |
+
- limit_b (float): Budget limit in billions
|
| 252 |
+
- remaining_b (float): Remaining budget in billions
|
| 253 |
+
- status (str): "pass" if within budget, "over_budget" otherwise
|
| 254 |
+
"""
|
| 255 |
chosen = stack or active_stack(False)
|
| 256 |
total = round(sum(model.params_b for model in chosen), 2)
|
| 257 |
return {
|
|
|
|
| 263 |
|
| 264 |
|
| 265 |
def catalog_summary(adult_mode: bool = False) -> dict[str, int | float | str]:
|
| 266 |
+
"""
|
| 267 |
+
Generates a summary of visible catalog entries and active model parameter budget.
|
| 268 |
+
|
| 269 |
+
Returns:
|
| 270 |
+
dict: A dictionary containing visible model and adapter counts, adult catalog status ("enabled" or "hidden"), and parameter budget metrics including total budget used, limit, remaining, and status ("pass" or "over_budget").
|
| 271 |
+
"""
|
| 272 |
models, adapters = filter_catalog(adult_mode)
|
| 273 |
budget = parameter_budget(active_stack(adult_mode))
|
| 274 |
return {
|
src/nexus_visual_weaver/exporter.py
CHANGED
|
@@ -6,7 +6,7 @@ import json
|
|
| 6 |
import os
|
| 7 |
import re
|
| 8 |
import time
|
| 9 |
-
from pathlib import Path
|
| 10 |
from typing import Any
|
| 11 |
|
| 12 |
from .catalog import active_stack, parameter_budget
|
|
@@ -20,6 +20,12 @@ ALLOWED_LOCAL_EXPORT_ROOTS = (
|
|
| 20 |
|
| 21 |
|
| 22 |
def _is_within(path: Path, root: Path) -> bool:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
try:
|
| 24 |
path.relative_to(root)
|
| 25 |
return True
|
|
@@ -28,6 +34,12 @@ def _is_within(path: Path, root: Path) -> bool:
|
|
| 28 |
|
| 29 |
|
| 30 |
def _safe_export_candidate(candidate: Path) -> Path | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
if not candidate.is_absolute():
|
| 32 |
candidate = REPO_ROOT / candidate
|
| 33 |
resolved = candidate.resolve(strict=False)
|
|
@@ -44,6 +56,12 @@ def _safe_export_candidate(candidate: Path) -> Path | None:
|
|
| 44 |
|
| 45 |
|
| 46 |
def _artifact_name(output_path: Any) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 47 |
if not output_path:
|
| 48 |
return None
|
| 49 |
return Path(str(output_path)).name
|
|
@@ -56,9 +74,15 @@ WINDOWS_PATH_RE = re.compile(r"[A-Za-z]:[\\/][^\s\"']+")
|
|
| 56 |
|
| 57 |
|
| 58 |
def _sanitize_text(value: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
text = SECRET_VALUE_RE.sub("[redacted_secret]", value)
|
| 60 |
text = CREDENTIAL_NAME_RE.sub("[redacted_credential_name]", text)
|
| 61 |
-
text = WINDOWS_PATH_RE.sub(lambda match: f"[local_path]/{
|
| 62 |
repo_text = str(REPO_ROOT)
|
| 63 |
if repo_text in text:
|
| 64 |
text = text.replace(repo_text, "[repo]")
|
|
@@ -70,6 +94,20 @@ def _sanitize_text(value: str) -> str:
|
|
| 70 |
|
| 71 |
|
| 72 |
def _safe_dict(value: Any, *, allow_size_bytes: bool = False) -> Any:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
if isinstance(value, dict):
|
| 74 |
clean: dict[str, Any] = {}
|
| 75 |
for key, item in value.items():
|
|
@@ -86,6 +124,13 @@ def _safe_dict(value: Any, *, allow_size_bytes: bool = False) -> Any:
|
|
| 86 |
|
| 87 |
|
| 88 |
def _safe_scan(scan: dict[str, Any]) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
return {
|
| 90 |
"status": scan.get("status"),
|
| 91 |
"scanner": scan.get("scanner"),
|
|
@@ -98,6 +143,15 @@ def _safe_scan(scan: dict[str, Any]) -> dict[str, Any]:
|
|
| 98 |
|
| 99 |
|
| 100 |
def _safe_provider(provider: dict[str, Any] | None) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
provider = provider or {}
|
| 102 |
evidence = provider.get("evidence") if isinstance(provider.get("evidence"), dict) else {}
|
| 103 |
return {
|
|
@@ -113,6 +167,12 @@ def _safe_provider(provider: dict[str, Any] | None) -> dict[str, Any]:
|
|
| 113 |
|
| 114 |
|
| 115 |
def _safe_reference_metadata(records: Any) -> list[dict[str, Any]]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
if not isinstance(records, list):
|
| 117 |
return []
|
| 118 |
cleaned: list[dict[str, Any]] = []
|
|
@@ -139,6 +199,13 @@ def _safe_reference_metadata(records: Any) -> list[dict[str, Any]]:
|
|
| 139 |
|
| 140 |
|
| 141 |
def export_root() -> Path:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 142 |
requested = os.environ.get("NEXUS_EXPORT_DIR")
|
| 143 |
candidates = [Path(requested)] if requested else []
|
| 144 |
if Path("/data").exists():
|
|
@@ -165,6 +232,19 @@ def write_export_packet(
|
|
| 165 |
operator_state: dict[str, Any],
|
| 166 |
adult_mode: bool,
|
| 167 |
) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 168 |
run_id = getattr(getattr(run, "checkpoint", None), "checkpoint_id", f"nw-{int(time.time())}")
|
| 169 |
run_adult_mode = bool(getattr(getattr(run, "request", None), "adult_mode", adult_mode))
|
| 170 |
stack = list(getattr(run, "model_stack", None) or active_stack(run_adult_mode))
|
|
|
|
| 6 |
import os
|
| 7 |
import re
|
| 8 |
import time
|
| 9 |
+
from pathlib import Path, PureWindowsPath
|
| 10 |
from typing import Any
|
| 11 |
|
| 12 |
from .catalog import active_stack, parameter_budget
|
|
|
|
| 20 |
|
| 21 |
|
| 22 |
def _is_within(path: Path, root: Path) -> bool:
|
| 23 |
+
"""
|
| 24 |
+
Checks if a path is within a given root directory.
|
| 25 |
+
|
| 26 |
+
Returns:
|
| 27 |
+
True if the path is within the root directory, False otherwise.
|
| 28 |
+
"""
|
| 29 |
try:
|
| 30 |
path.relative_to(root)
|
| 31 |
return True
|
|
|
|
| 34 |
|
| 35 |
|
| 36 |
def _safe_export_candidate(candidate: Path) -> Path | None:
|
| 37 |
+
"""
|
| 38 |
+
Validates and resolves an export directory candidate against allowed roots.
|
| 39 |
+
|
| 40 |
+
Returns:
|
| 41 |
+
Path | None: The resolved path if within an allowed export root, `None` otherwise.
|
| 42 |
+
"""
|
| 43 |
if not candidate.is_absolute():
|
| 44 |
candidate = REPO_ROOT / candidate
|
| 45 |
resolved = candidate.resolve(strict=False)
|
|
|
|
| 56 |
|
| 57 |
|
| 58 |
def _artifact_name(output_path: Any) -> str | None:
|
| 59 |
+
"""
|
| 60 |
+
Extract the filename from a file path.
|
| 61 |
+
|
| 62 |
+
Returns:
|
| 63 |
+
filename (str | None): The filename if output_path is provided, None otherwise.
|
| 64 |
+
"""
|
| 65 |
if not output_path:
|
| 66 |
return None
|
| 67 |
return Path(str(output_path)).name
|
|
|
|
| 74 |
|
| 75 |
|
| 76 |
def _sanitize_text(value: str) -> str:
|
| 77 |
+
"""
|
| 78 |
+
Redacts sensitive information and normalizes text for safe export.
|
| 79 |
+
|
| 80 |
+
Returns:
|
| 81 |
+
str: Text with secrets, credentials, and paths replaced with placeholders, truncated to 1000 characters if necessary.
|
| 82 |
+
"""
|
| 83 |
text = SECRET_VALUE_RE.sub("[redacted_secret]", value)
|
| 84 |
text = CREDENTIAL_NAME_RE.sub("[redacted_credential_name]", text)
|
| 85 |
+
text = WINDOWS_PATH_RE.sub(lambda match: f"[local_path]/{PureWindowsPath(match.group(0)).name}", text)
|
| 86 |
repo_text = str(REPO_ROOT)
|
| 87 |
if repo_text in text:
|
| 88 |
text = text.replace(repo_text, "[repo]")
|
|
|
|
| 94 |
|
| 95 |
|
| 96 |
def _safe_dict(value: Any, *, allow_size_bytes: bool = False) -> Any:
|
| 97 |
+
"""
|
| 98 |
+
Recursively sanitize data structures to remove sensitive keys and redact sensitive values.
|
| 99 |
+
|
| 100 |
+
Dictionary keys matching sensitive patterns are dropped unless the key is 'size_bytes' and
|
| 101 |
+
allow_size_bytes is True. Lists are limited to the first 40 elements. String values are sanitized
|
| 102 |
+
to remove secrets and credentials. Other types are returned unchanged.
|
| 103 |
+
|
| 104 |
+
Parameters:
|
| 105 |
+
allow_size_bytes (bool): If True, preserves the 'size_bytes' key in dictionaries even if it
|
| 106 |
+
matches a sensitive pattern. Defaults to False.
|
| 107 |
+
|
| 108 |
+
Returns:
|
| 109 |
+
The sanitized input value with sensitive data redacted and sensitive keys removed.
|
| 110 |
+
"""
|
| 111 |
if isinstance(value, dict):
|
| 112 |
clean: dict[str, Any] = {}
|
| 113 |
for key, item in value.items():
|
|
|
|
| 124 |
|
| 125 |
|
| 126 |
def _safe_scan(scan: dict[str, Any]) -> dict[str, Any]:
|
| 127 |
+
"""
|
| 128 |
+
Extract and sanitize selected fields from a scan dictionary.
|
| 129 |
+
|
| 130 |
+
Returns:
|
| 131 |
+
A dictionary containing status, scanner, export_gate, extension, magic,
|
| 132 |
+
findings, and purification_actions.
|
| 133 |
+
"""
|
| 134 |
return {
|
| 135 |
"status": scan.get("status"),
|
| 136 |
"scanner": scan.get("scanner"),
|
|
|
|
| 143 |
|
| 144 |
|
| 145 |
def _safe_provider(provider: dict[str, Any] | None) -> dict[str, Any]:
|
| 146 |
+
"""
|
| 147 |
+
Normalize provider metadata by extracting safe fields and sanitizing sensitive data.
|
| 148 |
+
|
| 149 |
+
Parameters:
|
| 150 |
+
provider: Provider metadata dict, or None.
|
| 151 |
+
|
| 152 |
+
Returns:
|
| 153 |
+
Normalized provider dict with extracted and sanitized fields.
|
| 154 |
+
"""
|
| 155 |
provider = provider or {}
|
| 156 |
evidence = provider.get("evidence") if isinstance(provider.get("evidence"), dict) else {}
|
| 157 |
return {
|
|
|
|
| 167 |
|
| 168 |
|
| 169 |
def _safe_reference_metadata(records: Any) -> list[dict[str, Any]]:
|
| 170 |
+
"""
|
| 171 |
+
Filters and sanitizes reference metadata records.
|
| 172 |
+
|
| 173 |
+
Returns:
|
| 174 |
+
A list of sanitized reference metadata dicts, limited to the first 20 records.
|
| 175 |
+
"""
|
| 176 |
if not isinstance(records, list):
|
| 177 |
return []
|
| 178 |
cleaned: list[dict[str, Any]] = []
|
|
|
|
| 199 |
|
| 200 |
|
| 201 |
def export_root() -> Path:
|
| 202 |
+
"""
|
| 203 |
+
Selects and creates a permitted directory for writing export JSON packets.
|
| 204 |
+
|
| 205 |
+
Returns:
|
| 206 |
+
Path: An existing export directory, prioritizing environment configuration
|
| 207 |
+
and system locations over the repository fallback.
|
| 208 |
+
"""
|
| 209 |
requested = os.environ.get("NEXUS_EXPORT_DIR")
|
| 210 |
candidates = [Path(requested)] if requested else []
|
| 211 |
if Path("/data").exists():
|
|
|
|
| 232 |
operator_state: dict[str, Any],
|
| 233 |
adult_mode: bool,
|
| 234 |
) -> dict[str, Any]:
|
| 235 |
+
"""
|
| 236 |
+
Builds a sanitized JSON export packet and writes it to disk.
|
| 237 |
+
|
| 238 |
+
Sanitizes sensitive metadata from run, scan, and operator state, then writes the
|
| 239 |
+
resulting packet to a controlled export directory.
|
| 240 |
+
|
| 241 |
+
Parameters:
|
| 242 |
+
run: Run object containing checkpoint, request, model_stack, and refined_prompt data.
|
| 243 |
+
|
| 244 |
+
Returns:
|
| 245 |
+
Dictionary with "path" (str) pointing to the written JSON file and "packet" (dict)
|
| 246 |
+
containing the constructed export packet.
|
| 247 |
+
"""
|
| 248 |
run_id = getattr(getattr(run, "checkpoint", None), "checkpoint_id", f"nw-{int(time.time())}")
|
| 249 |
run_adult_mode = bool(getattr(getattr(run, "request", None), "adult_mode", adult_mode))
|
| 250 |
stack = list(getattr(run, "model_stack", None) or active_stack(run_adult_mode))
|
src/nexus_visual_weaver/hf_runtime.py
CHANGED
|
@@ -40,6 +40,12 @@ class HFGenerationResult:
|
|
| 40 |
primary_error: str | None = None
|
| 41 |
|
| 42 |
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
return asdict(self)
|
| 44 |
|
| 45 |
|
|
@@ -75,10 +81,24 @@ def _short_error(exc: BaseException) -> str:
|
|
| 75 |
|
| 76 |
|
| 77 |
def _hf_token() -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
return os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
|
| 79 |
|
| 80 |
|
| 81 |
def active_flux_repo_id() -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 82 |
configured = os.environ.get("NEXUS_FLUX_REPO_ID")
|
| 83 |
if configured:
|
| 84 |
return configured
|
|
@@ -88,6 +108,20 @@ def active_flux_repo_id() -> str:
|
|
| 88 |
|
| 89 |
|
| 90 |
def _repo_candidates(repo_id: str) -> list[str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
candidates = [repo_id]
|
| 92 |
if repo_id != TINY_TITAN_FLUX_REPO_ID and os.environ.get("NEXUS_DISABLE_TINY_TITAN_FALLBACK") != "1":
|
| 93 |
candidates.append(TINY_TITAN_FLUX_REPO_ID)
|
|
@@ -95,6 +129,18 @@ def _repo_candidates(repo_id: str) -> list[str]:
|
|
| 95 |
|
| 96 |
|
| 97 |
def _get_flux_pipe(repo_id: str, torch_module: Any, pipeline_cls: Any, token: str | None) -> Any:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
with _PIPELINE_CACHE_LOCK:
|
| 99 |
cached = _PIPELINE_CACHE.get(repo_id)
|
| 100 |
if cached is not None:
|
|
@@ -106,12 +152,32 @@ def _get_flux_pipe(repo_id: str, torch_module: Any, pipeline_cls: Any, token: st
|
|
| 106 |
|
| 107 |
|
| 108 |
def _adapter_recipe(repo_id: str | None) -> AdapterRecipe | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
if not repo_id:
|
| 110 |
return None
|
| 111 |
return next((recipe for recipe in ADAPTER_CATALOG if recipe.repo_id == repo_id), None)
|
| 112 |
|
| 113 |
|
| 114 |
def default_lora_repo_id(target_repo_id: str) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 115 |
for recipe in ADAPTER_CATALOG:
|
| 116 |
compatible_ids = {recipe.adapter_for, *recipe.compatible_repo_ids}
|
| 117 |
if recipe.runtime_enabled and not recipe.adult_only and not recipe.requires_image and target_repo_id in compatible_ids:
|
|
@@ -129,6 +195,12 @@ def generate_flux_image(
|
|
| 129 |
lora_repo_id: str | None = None,
|
| 130 |
adult_mode: bool = False,
|
| 131 |
) -> HFGenerationResult:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
repo_id = active_flux_repo_id()
|
| 133 |
selected_lora = lora_repo_id if lora_repo_id is not None else default_lora_repo_id(repo_id)
|
| 134 |
recipe = _adapter_recipe(selected_lora)
|
|
|
|
| 40 |
primary_error: str | None = None
|
| 41 |
|
| 42 |
def to_dict(self) -> dict[str, Any]:
|
| 43 |
+
"""
|
| 44 |
+
Convert the result to a dictionary.
|
| 45 |
+
|
| 46 |
+
Returns:
|
| 47 |
+
The result as a dictionary.
|
| 48 |
+
"""
|
| 49 |
return asdict(self)
|
| 50 |
|
| 51 |
|
|
|
|
| 81 |
|
| 82 |
|
| 83 |
def _hf_token() -> str | None:
|
| 84 |
+
"""
|
| 85 |
+
Retrieve the HuggingFace authentication token from environment variables.
|
| 86 |
+
|
| 87 |
+
Checks HF_TOKEN first, then HUGGING_FACE_HUB_TOKEN.
|
| 88 |
+
|
| 89 |
+
Returns:
|
| 90 |
+
str | None: The token if available, None otherwise.
|
| 91 |
+
"""
|
| 92 |
return os.environ.get("HF_TOKEN") or os.environ.get("HUGGING_FACE_HUB_TOKEN")
|
| 93 |
|
| 94 |
|
| 95 |
def active_flux_repo_id() -> str:
|
| 96 |
+
"""
|
| 97 |
+
Determines the FLUX repository ID based on environment configuration.
|
| 98 |
+
|
| 99 |
+
Returns:
|
| 100 |
+
str: The FLUX repository ID to use for image generation.
|
| 101 |
+
"""
|
| 102 |
configured = os.environ.get("NEXUS_FLUX_REPO_ID")
|
| 103 |
if configured:
|
| 104 |
return configured
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
def _repo_candidates(repo_id: str) -> list[str]:
|
| 111 |
+
"""
|
| 112 |
+
Return a list of repository candidates for generation attempts.
|
| 113 |
+
|
| 114 |
+
The primary repo_id is always included. If repo_id is not the Tiny Titan
|
| 115 |
+
model and fallback is not disabled via NEXUS_DISABLE_TINY_TITAN_FALLBACK,
|
| 116 |
+
the Tiny Titan repository is appended as a fallback candidate.
|
| 117 |
+
|
| 118 |
+
Parameters:
|
| 119 |
+
repo_id (str): The primary FLUX model repository identifier.
|
| 120 |
+
|
| 121 |
+
Returns:
|
| 122 |
+
list[str]: Repository IDs to attempt, starting with the primary repo
|
| 123 |
+
and optionally including a fallback candidate.
|
| 124 |
+
"""
|
| 125 |
candidates = [repo_id]
|
| 126 |
if repo_id != TINY_TITAN_FLUX_REPO_ID and os.environ.get("NEXUS_DISABLE_TINY_TITAN_FALLBACK") != "1":
|
| 127 |
candidates.append(TINY_TITAN_FLUX_REPO_ID)
|
|
|
|
| 129 |
|
| 130 |
|
| 131 |
def _get_flux_pipe(repo_id: str, torch_module: Any, pipeline_cls: Any, token: str | None) -> Any:
|
| 132 |
+
"""
|
| 133 |
+
Get or load a cached FLUX pipeline from a Hugging Face repository.
|
| 134 |
+
|
| 135 |
+
Maintains a thread-safe cache of pipelines keyed by repository ID to avoid repeated loading.
|
| 136 |
+
|
| 137 |
+
Parameters:
|
| 138 |
+
torch_module: The torch module, providing the dtype for the pipeline.
|
| 139 |
+
pipeline_cls: The pipeline class (e.g., Flux2KleinPipeline) to instantiate.
|
| 140 |
+
|
| 141 |
+
Returns:
|
| 142 |
+
A FLUX pipeline instance.
|
| 143 |
+
"""
|
| 144 |
with _PIPELINE_CACHE_LOCK:
|
| 145 |
cached = _PIPELINE_CACHE.get(repo_id)
|
| 146 |
if cached is not None:
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
def _adapter_recipe(repo_id: str | None) -> AdapterRecipe | None:
|
| 155 |
+
"""
|
| 156 |
+
Retrieves an adapter recipe from the catalog by repository identifier.
|
| 157 |
+
|
| 158 |
+
Returns:
|
| 159 |
+
AdapterRecipe | None: The matching adapter recipe, or None if no recipe is found.
|
| 160 |
+
"""
|
| 161 |
if not repo_id:
|
| 162 |
return None
|
| 163 |
return next((recipe for recipe in ADAPTER_CATALOG if recipe.repo_id == repo_id), None)
|
| 164 |
|
| 165 |
|
| 166 |
def default_lora_repo_id(target_repo_id: str) -> str | None:
|
| 167 |
+
"""
|
| 168 |
+
Selects the default LoRA adapter compatible with a FLUX repository.
|
| 169 |
+
|
| 170 |
+
Returns the repository ID of the first adapter that is runtime-enabled, is not
|
| 171 |
+
adult-only, does not require an input image, and is compatible with the target
|
| 172 |
+
repository.
|
| 173 |
+
|
| 174 |
+
Parameters:
|
| 175 |
+
target_repo_id (str): The FLUX repository ID to find a compatible adapter for.
|
| 176 |
+
|
| 177 |
+
Returns:
|
| 178 |
+
The repository ID of the first matching adapter, or `None` if no compatible
|
| 179 |
+
adapter is found.
|
| 180 |
+
"""
|
| 181 |
for recipe in ADAPTER_CATALOG:
|
| 182 |
compatible_ids = {recipe.adapter_for, *recipe.compatible_repo_ids}
|
| 183 |
if recipe.runtime_enabled and not recipe.adult_only and not recipe.requires_image and target_repo_id in compatible_ids:
|
|
|
|
| 195 |
lora_repo_id: str | None = None,
|
| 196 |
adult_mode: bool = False,
|
| 197 |
) -> HFGenerationResult:
|
| 198 |
+
"""
|
| 199 |
+
Generate a FLUX.2 image from a text prompt with optional LoRA adapter application.
|
| 200 |
+
|
| 201 |
+
Returns:
|
| 202 |
+
HFGenerationResult: Immutable container with generation status, image output path, execution latency, and LoRA application results.
|
| 203 |
+
"""
|
| 204 |
repo_id = active_flux_repo_id()
|
| 205 |
selected_lora = lora_repo_id if lora_repo_id is not None else default_lora_repo_id(repo_id)
|
| 206 |
recipe = _adapter_recipe(selected_lora)
|
src/nexus_visual_weaver/lora_adapter.py
CHANGED
|
@@ -9,6 +9,12 @@ from .schema import AdapterRecipe
|
|
| 9 |
|
| 10 |
|
| 11 |
def _status(status: str, recipe: AdapterRecipe | None = None, **extra: Any) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
payload: dict[str, Any] = {
|
| 13 |
"status": status,
|
| 14 |
"repo_id": recipe.repo_id if recipe else None,
|
|
@@ -20,6 +26,18 @@ def _status(status: str, recipe: AdapterRecipe | None = None, **extra: Any) -> d
|
|
| 20 |
|
| 21 |
|
| 22 |
def _short_error(exc: BaseException) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
text = str(exc).replace("\n", " ").strip()
|
| 24 |
if len(text) > 240:
|
| 25 |
text = text[:237] + "..."
|
|
@@ -27,10 +45,24 @@ def _short_error(exc: BaseException) -> str:
|
|
| 27 |
|
| 28 |
|
| 29 |
def adapter_to_dict(recipe: AdapterRecipe) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
return asdict(recipe)
|
| 31 |
|
| 32 |
|
| 33 |
def is_compatible(pipe: Any, recipe: AdapterRecipe, target_repo_id: str, *, adult_mode: bool = False) -> bool:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
if not recipe.runtime_enabled:
|
| 35 |
return False
|
| 36 |
if recipe.adult_only and not adult_mode:
|
|
@@ -54,6 +86,16 @@ def load_and_apply(
|
|
| 54 |
adult_mode: bool = False,
|
| 55 |
adapter_name: str = "nexus_style",
|
| 56 |
) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
if recipe is None:
|
| 58 |
return _status("disabled", message="No LoRA adapter selected for this run.")
|
| 59 |
if recipe.adult_only and not adult_mode:
|
|
@@ -78,6 +120,9 @@ def load_and_apply(
|
|
| 78 |
|
| 79 |
|
| 80 |
def unload_all(pipe: Any) -> None:
|
|
|
|
|
|
|
|
|
|
| 81 |
try:
|
| 82 |
if hasattr(pipe, "unload_lora_weights"):
|
| 83 |
pipe.unload_lora_weights()
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
def _status(status: str, recipe: AdapterRecipe | None = None, **extra: Any) -> dict[str, Any]:
|
| 12 |
+
"""
|
| 13 |
+
Build a status dictionary with adapter metadata and additional fields.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
dict[str, Any]: A dictionary containing the status and optional recipe fields (repo_id, adapter_for, weight), merged with any extra keyword arguments.
|
| 17 |
+
"""
|
| 18 |
payload: dict[str, Any] = {
|
| 19 |
"status": status,
|
| 20 |
"repo_id": recipe.repo_id if recipe else None,
|
|
|
|
| 26 |
|
| 27 |
|
| 28 |
def _short_error(exc: BaseException) -> str:
|
| 29 |
+
"""
|
| 30 |
+
Format an exception message with truncation for compact display.
|
| 31 |
+
|
| 32 |
+
Returns the exception class name and message as "{ClassName}: {message}",
|
| 33 |
+
truncated to 240 characters.
|
| 34 |
+
|
| 35 |
+
Parameters:
|
| 36 |
+
exc (BaseException): The exception to format.
|
| 37 |
+
|
| 38 |
+
Returns:
|
| 39 |
+
str: The formatted exception message.
|
| 40 |
+
"""
|
| 41 |
text = str(exc).replace("\n", " ").strip()
|
| 42 |
if len(text) > 240:
|
| 43 |
text = text[:237] + "..."
|
|
|
|
| 45 |
|
| 46 |
|
| 47 |
def adapter_to_dict(recipe: AdapterRecipe) -> dict[str, Any]:
|
| 48 |
+
"""
|
| 49 |
+
Convert an AdapterRecipe instance to a dictionary.
|
| 50 |
+
|
| 51 |
+
Returns:
|
| 52 |
+
dict[str, Any]: Dictionary representation of the recipe's fields.
|
| 53 |
+
"""
|
| 54 |
return asdict(recipe)
|
| 55 |
|
| 56 |
|
| 57 |
def is_compatible(pipe: Any, recipe: AdapterRecipe, target_repo_id: str, *, adult_mode: bool = False) -> bool:
|
| 58 |
+
"""
|
| 59 |
+
Determines whether a LoRA adapter is compatible with a pipeline and target model.
|
| 60 |
+
|
| 61 |
+
Returns:
|
| 62 |
+
`true` if the recipe is runtime-enabled, not blocked by adult-mode restrictions,
|
| 63 |
+
does not require image input, the pipeline has LoRA support, and the target model
|
| 64 |
+
is compatible; `false` otherwise.
|
| 65 |
+
"""
|
| 66 |
if not recipe.runtime_enabled:
|
| 67 |
return False
|
| 68 |
if recipe.adult_only and not adult_mode:
|
|
|
|
| 86 |
adult_mode: bool = False,
|
| 87 |
adapter_name: str = "nexus_style",
|
| 88 |
) -> dict[str, Any]:
|
| 89 |
+
"""
|
| 90 |
+
Load and apply a LoRA adapter to a pipeline when permitted, returning structured operation status.
|
| 91 |
+
|
| 92 |
+
Parameters:
|
| 93 |
+
recipe: Adapter configuration. If None, the function returns a disabled status without attempting to load.
|
| 94 |
+
target_repo_id: The model repository ID to verify adapter compatibility against.
|
| 95 |
+
|
| 96 |
+
Returns:
|
| 97 |
+
A dictionary with keys: status (disabled, skipped_incompatible, unsupported_pipeline, loaded, or failed), repo_id, adapter_for, weight, adapter_name, and message.
|
| 98 |
+
"""
|
| 99 |
if recipe is None:
|
| 100 |
return _status("disabled", message="No LoRA adapter selected for this run.")
|
| 101 |
if recipe.adult_only and not adult_mode:
|
|
|
|
| 120 |
|
| 121 |
|
| 122 |
def unload_all(pipe: Any) -> None:
|
| 123 |
+
"""
|
| 124 |
+
Unload all LoRA adapter weights from the pipeline if supported.
|
| 125 |
+
"""
|
| 126 |
try:
|
| 127 |
if hasattr(pipe, "unload_lora_weights"):
|
| 128 |
pipe.unload_lora_weights()
|
src/nexus_visual_weaver/model_relay.py
CHANGED
|
@@ -179,6 +179,14 @@ class WeaverModelRelay:
|
|
| 179 |
public_demo: bool = True,
|
| 180 |
strategy: str = "quality_first",
|
| 181 |
) -> LaneDecision:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
lane = self.normalize_lane(lane)
|
| 183 |
strategy = self.normalize_strategy(strategy)
|
| 184 |
budget_b = float(budget if budget is not None else (32.0 if lane in PINNED_LANES else DEFAULT_ROTATION_BUDGET_B))
|
|
@@ -430,6 +438,12 @@ class WeaverModelRelay:
|
|
| 430 |
|
| 431 |
|
| 432 |
def default_model_records() -> list[ModelRecord]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
return [
|
| 434 |
ModelRecord(
|
| 435 |
model_id="flux2-klein-9b-quality",
|
|
|
|
| 179 |
public_demo: bool = True,
|
| 180 |
strategy: str = "quality_first",
|
| 181 |
) -> LaneDecision:
|
| 182 |
+
"""
|
| 183 |
+
Selects a helper model for the given lane based on budget, quota, and selection strategy.
|
| 184 |
+
|
| 185 |
+
Returns:
|
| 186 |
+
LaneDecision: A decision containing the selected primary model (if any), fallback
|
| 187 |
+
options, the selection strategy used, selection reasoning, expected cost estimate,
|
| 188 |
+
quota impact details, and information about skipped ineligible candidates.
|
| 189 |
+
"""
|
| 190 |
lane = self.normalize_lane(lane)
|
| 191 |
strategy = self.normalize_strategy(strategy)
|
| 192 |
budget_b = float(budget if budget is not None else (32.0 if lane in PINNED_LANES else DEFAULT_ROTATION_BUDGET_B))
|
|
|
|
| 438 |
|
| 439 |
|
| 440 |
def default_model_records() -> list[ModelRecord]:
|
| 441 |
+
"""
|
| 442 |
+
Provides the default registry of helper models across all lanes.
|
| 443 |
+
|
| 444 |
+
Returns:
|
| 445 |
+
list[ModelRecord]: A list of preconfigured model records spanning pinned lanes (image_generation, grounding, security) and rotatable lanes (prompt_router, taste_judge, audio_lore_tts, video_repair, hf_catalog_research, modal_job_runner, private_image_research), each with quota limits, performance metrics, and fallback chains.
|
| 446 |
+
"""
|
| 447 |
return [
|
| 448 |
ModelRecord(
|
| 449 |
model_id="flux2-klein-9b-quality",
|
src/nexus_visual_weaver/planner.py
CHANGED
|
@@ -20,6 +20,20 @@ def build_command_center_run(
|
|
| 20 |
creator_controls: dict | None = None,
|
| 21 |
reference_metadata: list[dict] | None = None,
|
| 22 |
) -> GenerationRun:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
controls = creator_controls or {}
|
| 24 |
references = reference_metadata or []
|
| 25 |
request = CreativeRequest(
|
|
|
|
| 20 |
creator_controls: dict | None = None,
|
| 21 |
reference_metadata: list[dict] | None = None,
|
| 22 |
) -> GenerationRun:
|
| 23 |
+
"""
|
| 24 |
+
Assemble a complete generation run by processing a creative prompt through refinement, outfit generation, inspection, and planning stages, then recommend approval or revision via a human checkpoint.
|
| 25 |
+
|
| 26 |
+
Parameters:
|
| 27 |
+
prompt: The creative prompt to generate from.
|
| 28 |
+
mode: Operation mode (e.g., "Strict", "Frontier"); determines checkpoint requirements.
|
| 29 |
+
video_preset: Video generation preset identifier.
|
| 30 |
+
adult_mode: Whether to enable adult content handling.
|
| 31 |
+
creator_controls: Optional dictionary of creator-specified controls (e.g., wardrobe preferences).
|
| 32 |
+
reference_metadata: Optional list of reference metadata dictionaries with id, basename, or url_hash keys.
|
| 33 |
+
|
| 34 |
+
Returns:
|
| 35 |
+
A GenerationRun containing the refined prompt, generated outfit, inspection results, model stack, adapters, video plan, lore components, and a human checkpoint with approval recommendation based on a computed trust score.
|
| 36 |
+
"""
|
| 37 |
controls = creator_controls or {}
|
| 38 |
references = reference_metadata or []
|
| 39 |
request = CreativeRequest(
|
src/nexus_visual_weaver/provider_runtime.py
CHANGED
|
@@ -31,10 +31,22 @@ class ProviderJudgeResult:
|
|
| 31 |
latency_seconds: float | None = None
|
| 32 |
|
| 33 |
def to_dict(self) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
return asdict(self)
|
| 35 |
|
| 36 |
|
| 37 |
def _short_error(exc: BaseException) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
text = str(exc).replace("\n", " ").strip()
|
| 39 |
if len(text) > 360:
|
| 40 |
text = text[:357] + "..."
|
|
@@ -42,6 +54,12 @@ def _short_error(exc: BaseException) -> str:
|
|
| 42 |
|
| 43 |
|
| 44 |
def _image_data_url(path: str | None) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
if not path:
|
| 46 |
return None
|
| 47 |
target = Path(path)
|
|
@@ -57,6 +75,19 @@ def _image_data_url(path: str | None) -> str | None:
|
|
| 57 |
|
| 58 |
|
| 59 |
def _post_json(url: str, token: str, payload: dict[str, Any], timeout: float) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
parsed = urllib.parse.urlparse(url)
|
| 61 |
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
| 62 |
raise ValueError(f"Invalid URL: expected http(s) URL with host, got {url!r}.")
|
|
@@ -75,6 +106,17 @@ def _post_json(url: str, token: str, payload: dict[str, Any], timeout: float) ->
|
|
| 75 |
|
| 76 |
|
| 77 |
def _extract_content(response: dict[str, Any]) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
choices = response.get("choices") or []
|
| 79 |
if not choices:
|
| 80 |
return ""
|
|
@@ -86,6 +128,14 @@ def _extract_content(response: dict[str, Any]) -> str:
|
|
| 86 |
|
| 87 |
|
| 88 |
def _safe_json_from_text(text: str) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 89 |
stripped = text.strip()
|
| 90 |
if not stripped:
|
| 91 |
return {}
|
|
@@ -108,6 +158,23 @@ def judge_with_minicpm(
|
|
| 108 |
wardrobe_summary: str,
|
| 109 |
timeout: float = 45.0,
|
| 110 |
) -> ProviderJudgeResult:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
base_url = os.environ.get("MINICPM_BASE_URL", "").rstrip("/")
|
| 112 |
token = os.environ.get("MINICPM_API_KEY") or os.environ.get("OPENBMB_API_KEY")
|
| 113 |
model = os.environ.get("MINICPM_MODEL", "MiniCPM-V-4.6")
|
|
@@ -187,6 +254,20 @@ def judge_with_nemotron(
|
|
| 187 |
minicpm_result: dict[str, Any] | None = None,
|
| 188 |
timeout: float = 45.0,
|
| 189 |
) -> ProviderJudgeResult:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
base_url = os.environ.get("NEMOTRON_BASE_URL", "").rstrip("/")
|
| 191 |
token = os.environ.get("NEMOTRON_API_KEY") or os.environ.get("NVIDIA_API_KEY")
|
| 192 |
model = os.environ.get("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-Parse-v1.2")
|
|
|
|
| 31 |
latency_seconds: float | None = None
|
| 32 |
|
| 33 |
def to_dict(self) -> dict[str, Any]:
|
| 34 |
+
"""
|
| 35 |
+
Convert the dataclass instance to a dictionary.
|
| 36 |
+
|
| 37 |
+
Returns:
|
| 38 |
+
dict[str, Any]: A dictionary representation of the dataclass fields.
|
| 39 |
+
"""
|
| 40 |
return asdict(self)
|
| 41 |
|
| 42 |
|
| 43 |
def _short_error(exc: BaseException) -> str:
|
| 44 |
+
"""
|
| 45 |
+
Format an exception as a compact single-line error string.
|
| 46 |
+
|
| 47 |
+
Returns:
|
| 48 |
+
A string with the exception class name and message, truncated to approximately 360 characters.
|
| 49 |
+
"""
|
| 50 |
text = str(exc).replace("\n", " ").strip()
|
| 51 |
if len(text) > 360:
|
| 52 |
text = text[:357] + "..."
|
|
|
|
| 54 |
|
| 55 |
|
| 56 |
def _image_data_url(path: str | None) -> str | None:
|
| 57 |
+
"""
|
| 58 |
+
Generates a data URL from a local image file.
|
| 59 |
+
|
| 60 |
+
Returns:
|
| 61 |
+
A data URL string if the file exists and is readable, None otherwise.
|
| 62 |
+
"""
|
| 63 |
if not path:
|
| 64 |
return None
|
| 65 |
target = Path(path)
|
|
|
|
| 75 |
|
| 76 |
|
| 77 |
def _post_json(url: str, token: str, payload: dict[str, Any], timeout: float) -> dict[str, Any]:
|
| 78 |
+
"""
|
| 79 |
+
Send an authenticated JSON POST request and return the parsed response.
|
| 80 |
+
|
| 81 |
+
Raises ValueError if the URL is not an HTTP or HTTPS URL with a host.
|
| 82 |
+
|
| 83 |
+
Parameters:
|
| 84 |
+
token (str): Authorization credential passed through the request header
|
| 85 |
+
payload (dict[str, Any]): Data to send as JSON in the request body
|
| 86 |
+
timeout (float): Request timeout in seconds
|
| 87 |
+
|
| 88 |
+
Returns:
|
| 89 |
+
dict[str, Any]: Parsed JSON response body
|
| 90 |
+
"""
|
| 91 |
parsed = urllib.parse.urlparse(url)
|
| 92 |
if parsed.scheme not in {"http", "https"} or not parsed.netloc:
|
| 93 |
raise ValueError(f"Invalid URL: expected http(s) URL with host, got {url!r}.")
|
|
|
|
| 106 |
|
| 107 |
|
| 108 |
def _extract_content(response: dict[str, Any]) -> str:
|
| 109 |
+
"""
|
| 110 |
+
Extract the message content from a chat-completions-style response.
|
| 111 |
+
|
| 112 |
+
If the extracted content is a string, returns it as-is. If it is another type, serializes it to JSON. Returns an empty string if no choices are present in the response.
|
| 113 |
+
|
| 114 |
+
Parameters:
|
| 115 |
+
response: A dictionary representing a chat-completions API response.
|
| 116 |
+
|
| 117 |
+
Returns:
|
| 118 |
+
The extracted content as a string, or an empty string if unavailable.
|
| 119 |
+
"""
|
| 120 |
choices = response.get("choices") or []
|
| 121 |
if not choices:
|
| 122 |
return ""
|
|
|
|
| 128 |
|
| 129 |
|
| 130 |
def _safe_json_from_text(text: str) -> dict[str, Any]:
|
| 131 |
+
"""
|
| 132 |
+
Extract and parse a JSON object from text, falling back to raw text summary.
|
| 133 |
+
|
| 134 |
+
Attempts to parse JSON from the input, extracting the outermost braces-delimited substring if necessary. If parsing fails or the result is not a dictionary, returns a dictionary with the input text (first 1200 characters) under the "raw_summary" key. Returns an empty dictionary if the input is empty after whitespace stripping.
|
| 135 |
+
|
| 136 |
+
Returns:
|
| 137 |
+
dict[str, Any]: Parsed JSON object if valid, or a dictionary with "raw_summary" key and text if parsing fails or result is not a dict.
|
| 138 |
+
"""
|
| 139 |
stripped = text.strip()
|
| 140 |
if not stripped:
|
| 141 |
return {}
|
|
|
|
| 158 |
wardrobe_summary: str,
|
| 159 |
timeout: float = 45.0,
|
| 160 |
) -> ProviderJudgeResult:
|
| 161 |
+
"""
|
| 162 |
+
Evaluate a generated image against visual and wardrobe requirements using MiniCPM-V.
|
| 163 |
+
|
| 164 |
+
Configuration is read from environment variables: MINICPM_BASE_URL, MINICPM_API_KEY
|
| 165 |
+
(or OPENBMB_API_KEY), and optionally MINICPM_MODEL.
|
| 166 |
+
|
| 167 |
+
Parameters:
|
| 168 |
+
prompt (str): Brief describing the visual and style requirements.
|
| 169 |
+
image_path (str | None): Path to the generated image file.
|
| 170 |
+
scan (dict[str, Any]): Metadata including 'export_gate' status.
|
| 171 |
+
wardrobe_summary (str): Wardrobe context and constraints.
|
| 172 |
+
timeout (float): Request timeout in seconds. Default is 45.0.
|
| 173 |
+
|
| 174 |
+
Returns:
|
| 175 |
+
ProviderJudgeResult: A result object with judgment status (success, failed,
|
| 176 |
+
missing_secret, no_artifact), provider metadata, evidence dict, and request latency.
|
| 177 |
+
"""
|
| 178 |
base_url = os.environ.get("MINICPM_BASE_URL", "").rstrip("/")
|
| 179 |
token = os.environ.get("MINICPM_API_KEY") or os.environ.get("OPENBMB_API_KEY")
|
| 180 |
model = os.environ.get("MINICPM_MODEL", "MiniCPM-V-4.6")
|
|
|
|
| 254 |
minicpm_result: dict[str, Any] | None = None,
|
| 255 |
timeout: float = 45.0,
|
| 256 |
) -> ProviderJudgeResult:
|
| 257 |
+
"""
|
| 258 |
+
Call Nemotron to generate structured evidence for a visual creation run.
|
| 259 |
+
|
| 260 |
+
Returns a standardized ProviderJudgeResult with status "success" (including parsed
|
| 261 |
+
evidence and measured latency), "missing_secret" if credentials are not configured,
|
| 262 |
+
or "failed" if the API call encounters an error.
|
| 263 |
+
|
| 264 |
+
Parameters:
|
| 265 |
+
minicpm_result (dict[str, Any] | None): Optional prior judgment output from MiniCPM.
|
| 266 |
+
timeout (float): Request timeout in seconds (default: 45.0).
|
| 267 |
+
|
| 268 |
+
Returns:
|
| 269 |
+
ProviderJudgeResult: Result with status, evidence, and measured latency.
|
| 270 |
+
"""
|
| 271 |
base_url = os.environ.get("NEMOTRON_BASE_URL", "").rstrip("/")
|
| 272 |
token = os.environ.get("NEMOTRON_API_KEY") or os.environ.get("NVIDIA_API_KEY")
|
| 273 |
model = os.environ.get("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-Parse-v1.2")
|
src/nexus_visual_weaver/render.py
CHANGED
|
@@ -12,6 +12,16 @@ from .schema import GenerationRun
|
|
| 12 |
|
| 13 |
|
| 14 |
def badge(label: str, tone: str = "neutral") -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
return f'<span class="nw-badge nw-{tone}">{escape(label)}</span>'
|
| 16 |
|
| 17 |
|
|
@@ -45,10 +55,23 @@ def _metric(label: str, value: str, tone: str = "neutral") -> str:
|
|
| 45 |
|
| 46 |
|
| 47 |
def _env_configured(*names: str) -> bool:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
return any(bool(os.environ.get(name)) for name in names)
|
| 49 |
|
| 50 |
|
| 51 |
def _provider_configured(base_url_name: str, *key_names: str) -> bool:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
return bool(os.environ.get(base_url_name)) and _env_configured(*key_names)
|
| 53 |
|
| 54 |
|
|
@@ -64,6 +87,17 @@ _SCAN_REDACTION_TERMS = (
|
|
| 64 |
|
| 65 |
|
| 66 |
def _redact_scan_text(value: object) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
text = str(value)
|
| 68 |
lowered = text.lower()
|
| 69 |
if any(term in lowered for term in _SCAN_REDACTION_TERMS):
|
|
@@ -74,6 +108,12 @@ def _redact_scan_text(value: object) -> str:
|
|
| 74 |
|
| 75 |
|
| 76 |
def _space_runtime_status() -> dict[str, str]:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
space_id = os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "local-preview"
|
| 78 |
hardware = os.environ.get("SPACE_HARDWARE") or os.environ.get("NEXUS_SPACE_HARDWARE") or "ZeroGPU"
|
| 79 |
bucket = "/data mounted" if os.path.isdir("/data") else "bucket optional"
|
|
@@ -96,6 +136,12 @@ def _space_runtime_status() -> dict[str, str]:
|
|
| 96 |
|
| 97 |
|
| 98 |
def _image_data_uri(path: str | None) -> str | None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
if not path:
|
| 100 |
return None
|
| 101 |
target = Path(path)
|
|
@@ -108,6 +154,12 @@ def _image_data_uri(path: str | None) -> str | None:
|
|
| 108 |
|
| 109 |
|
| 110 |
def render_command_header() -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
return f"""
|
| 112 |
<section class="nw-command-header">
|
| 113 |
<div>
|
|
@@ -127,12 +179,24 @@ def render_command_header() -> str:
|
|
| 127 |
|
| 128 |
|
| 129 |
def render_trust_strip(scan: dict | None = None, operator_state: dict | None = None) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
scan = scan or {"status": "idle", "export_gate": "pending", "findings": [], "purification_actions": []}
|
| 131 |
operator_state = operator_state or {}
|
| 132 |
status = str(scan.get("status", "idle")).upper()
|
| 133 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 134 |
checkpoint = str(operator_state.get("checkpoint", "pending")).replace("_", " ").title()
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
actions = [_redact_scan_text(item) for item in (scan.get("purification_actions") or [
|
| 137 |
"strip metadata before export",
|
| 138 |
"truncate PNG after IEND when needed",
|
|
@@ -161,13 +225,15 @@ def render_topbar(
|
|
| 161 |
operator_state: dict | None = None,
|
| 162 |
) -> str:
|
| 163 |
"""
|
| 164 |
-
|
| 165 |
|
| 166 |
Parameters:
|
| 167 |
-
relay_status: Dictionary
|
|
|
|
|
|
|
| 168 |
|
| 169 |
Returns:
|
| 170 |
-
HTML markup for the topbar
|
| 171 |
"""
|
| 172 |
summary = catalog_summary(adult_mode)
|
| 173 |
active = float(summary["active_b"])
|
|
@@ -218,6 +284,12 @@ def render_left_rail(active_section: str = "Forge") -> str:
|
|
| 218 |
|
| 219 |
|
| 220 |
def render_command_rail(active_section: str = "Forge") -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
section = escape(active_section)
|
| 222 |
hints = {
|
| 223 |
"Forge": ("Active Weave", "Prompt, judge, locate, generate, checkpoint."),
|
|
@@ -238,6 +310,22 @@ def render_command_rail(active_section: str = "Forge") -> str:
|
|
| 238 |
|
| 239 |
|
| 240 |
def render_workflow(run: GenerationRun | None = None, operator_state: dict | None = None) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 241 |
operator_state = operator_state or {}
|
| 242 |
score = run.checkpoint.trust_score if run else 0.82
|
| 243 |
checkpoint_id = run.checkpoint.checkpoint_id if run else "nw-dry-run"
|
|
@@ -345,6 +433,24 @@ def render_workflow(run: GenerationRun | None = None, operator_state: dict | Non
|
|
| 345 |
|
| 346 |
|
| 347 |
def render_artifact_lane(run: GenerationRun | None = None, scan: dict | None = None, operator_state: dict | None = None) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 348 |
scan = scan or {"status": "idle", "export_gate": "pending"}
|
| 349 |
operator_state = operator_state or {}
|
| 350 |
prompt_label = "Prompt proof"
|
|
@@ -431,14 +537,15 @@ def render_operations_panel(
|
|
| 431 |
"""
|
| 432 |
Render an operations panel with section-specific operation cards.
|
| 433 |
|
| 434 |
-
Generates an HTML section containing three operation cards tailored to the selected section
|
| 435 |
-
|
| 436 |
Invalid section names default to "Forge".
|
| 437 |
|
| 438 |
Parameters:
|
| 439 |
active_section (str): The section name determining which operation cards to display.
|
| 440 |
Valid sections are "Forge", "Wardrobe", "Lore", "Models", "Security", and "Runs".
|
| 441 |
adult_mode (bool): If True, scope label is "Private research scope"; otherwise "Public demo scope".
|
|
|
|
| 442 |
|
| 443 |
Returns:
|
| 444 |
str: HTML string representing the operations panel section.
|
|
@@ -531,6 +638,12 @@ def _short_repo(repo_id: str) -> str:
|
|
| 531 |
|
| 532 |
|
| 533 |
def _render_relay_panel(relay_status: dict | None = None) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 534 |
relay_status = relay_status or {}
|
| 535 |
pinned = relay_status.get("pinned", {})
|
| 536 |
decisions = relay_status.get("decisions", [])
|
|
@@ -577,14 +690,16 @@ def _render_relay_panel(relay_status: dict | None = None) -> str:
|
|
| 577 |
|
| 578 |
def render_provider_cards(relay_status: dict | None = None, adult_mode: bool = False) -> str:
|
| 579 |
"""
|
| 580 |
-
|
|
|
|
|
|
|
| 581 |
|
| 582 |
Parameters:
|
| 583 |
-
|
| 584 |
-
|
| 585 |
|
| 586 |
Returns:
|
| 587 |
-
|
| 588 |
"""
|
| 589 |
relay_status = relay_status or {}
|
| 590 |
decisions = relay_status.get("decisions", [])
|
|
@@ -646,6 +761,12 @@ def render_provider_cards(relay_status: dict | None = None, adult_mode: bool = F
|
|
| 646 |
|
| 647 |
|
| 648 |
def _scan_status_tone(scan_status: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 649 |
if scan_status == "pass":
|
| 650 |
return "pass"
|
| 651 |
if scan_status in {"review", "error"}:
|
|
@@ -659,6 +780,12 @@ def render_inspector(
|
|
| 659 |
relay_status: dict | None = None,
|
| 660 |
operator_state: dict | None = None,
|
| 661 |
) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 662 |
if run:
|
| 663 |
checks = [
|
| 664 |
("Patent Leather", True),
|
|
@@ -862,19 +989,18 @@ def render_dashboard_regions(
|
|
| 862 |
operator_state: dict | None = None,
|
| 863 |
) -> dict[str, str]:
|
| 864 |
"""
|
| 865 |
-
|
| 866 |
-
|
| 867 |
-
Returns a dictionary of HTML strings, each representing a rendered dashboard region.
|
| 868 |
|
| 869 |
Parameters:
|
| 870 |
-
run: A generation run
|
| 871 |
adult_mode: Whether to render adult-mode content.
|
| 872 |
-
scan: Scanner status and findings
|
| 873 |
-
relay_status: Model relay configuration
|
| 874 |
active_section: The currently active navigation section.
|
|
|
|
| 875 |
|
| 876 |
Returns:
|
| 877 |
-
dict[str, str]: HTML markup strings
|
| 878 |
"""
|
| 879 |
return {
|
| 880 |
"topbar": render_topbar(adult_mode, relay_status, scan, operator_state),
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
def badge(label: str, tone: str = "neutral") -> str:
|
| 15 |
+
"""
|
| 16 |
+
Generate an HTML badge element with a tone-specific CSS class.
|
| 17 |
+
|
| 18 |
+
Parameters:
|
| 19 |
+
label (str): The badge text content.
|
| 20 |
+
tone (str): A CSS class modifier for the badge style; defaults to "neutral".
|
| 21 |
+
|
| 22 |
+
Returns:
|
| 23 |
+
str: An HTML span element with the specified tone class.
|
| 24 |
+
"""
|
| 25 |
return f'<span class="nw-badge nw-{tone}">{escape(label)}</span>'
|
| 26 |
|
| 27 |
|
|
|
|
| 55 |
|
| 56 |
|
| 57 |
def _env_configured(*names: str) -> bool:
|
| 58 |
+
"""
|
| 59 |
+
Determine whether any of the provided environment variables are set and truthy.
|
| 60 |
+
|
| 61 |
+
Returns:
|
| 62 |
+
True if any of the environment variables exists and is truthy, False otherwise.
|
| 63 |
+
"""
|
| 64 |
return any(bool(os.environ.get(name)) for name in names)
|
| 65 |
|
| 66 |
|
| 67 |
def _provider_configured(base_url_name: str, *key_names: str) -> bool:
|
| 68 |
+
"""
|
| 69 |
+
Determines if a provider is configured with both a base URL and authentication keys.
|
| 70 |
+
|
| 71 |
+
Returns:
|
| 72 |
+
True if the base URL environment variable is set and at least one of the
|
| 73 |
+
provided key environment variables is set, False otherwise.
|
| 74 |
+
"""
|
| 75 |
return bool(os.environ.get(base_url_name)) and _env_configured(*key_names)
|
| 76 |
|
| 77 |
|
|
|
|
| 87 |
|
| 88 |
|
| 89 |
def _redact_scan_text(value: object) -> str:
|
| 90 |
+
"""
|
| 91 |
+
Sanitizes scan text by redacting sensitive content and truncating excessively long values.
|
| 92 |
+
|
| 93 |
+
Parameters:
|
| 94 |
+
value (object): A scan detail to sanitize.
|
| 95 |
+
|
| 96 |
+
Returns:
|
| 97 |
+
str: A redaction notice if the text contains sensitive terms; the original text truncated
|
| 98 |
+
to 177 characters plus "..." if it exceeds 180 characters; otherwise the original
|
| 99 |
+
text converted to a string.
|
| 100 |
+
"""
|
| 101 |
text = str(value)
|
| 102 |
lowered = text.lower()
|
| 103 |
if any(term in lowered for term in _SCAN_REDACTION_TERMS):
|
|
|
|
| 108 |
|
| 109 |
|
| 110 |
def _space_runtime_status() -> dict[str, str]:
|
| 111 |
+
"""
|
| 112 |
+
Determines the current runtime environment and active provider configuration.
|
| 113 |
+
|
| 114 |
+
Returns:
|
| 115 |
+
dict[str, str]: A dict with `space_id`, `hardware`, `bucket`, and `secrets` keys describing the deployment context and configured provider secrets.
|
| 116 |
+
"""
|
| 117 |
space_id = os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "local-preview"
|
| 118 |
hardware = os.environ.get("SPACE_HARDWARE") or os.environ.get("NEXUS_SPACE_HARDWARE") or "ZeroGPU"
|
| 119 |
bucket = "/data mounted" if os.path.isdir("/data") else "bucket optional"
|
|
|
|
| 136 |
|
| 137 |
|
| 138 |
def _image_data_uri(path: str | None) -> str | None:
|
| 139 |
+
"""
|
| 140 |
+
Create a data URI string from a file's contents, selecting MIME type by extension.
|
| 141 |
+
|
| 142 |
+
Returns:
|
| 143 |
+
data_uri (str | None): A data URI string with base64-encoded file contents if the path exists and is readable; `None` otherwise. MIME type is determined by file extension (.png, .jpg/.jpeg, .webp; otherwise application/octet-stream).
|
| 144 |
+
"""
|
| 145 |
if not path:
|
| 146 |
return None
|
| 147 |
target = Path(path)
|
|
|
|
| 154 |
|
| 155 |
|
| 156 |
def render_command_header() -> str:
|
| 157 |
+
"""
|
| 158 |
+
Render the command header section of the dashboard.
|
| 159 |
+
|
| 160 |
+
Returns:
|
| 161 |
+
An HTML string containing the command header, description, and status badges.
|
| 162 |
+
"""
|
| 163 |
return f"""
|
| 164 |
<section class="nw-command-header">
|
| 165 |
<div>
|
|
|
|
| 179 |
|
| 180 |
|
| 181 |
def render_trust_strip(scan: dict | None = None, operator_state: dict | None = None) -> str:
|
| 182 |
+
"""
|
| 183 |
+
Renders a trust model strip showing scan findings, export gate status, and safety checkpoint state.
|
| 184 |
+
|
| 185 |
+
Returns:
|
| 186 |
+
An HTML string for the trust strip section containing badge cards with redacted scan details.
|
| 187 |
+
"""
|
| 188 |
scan = scan or {"status": "idle", "export_gate": "pending", "findings": [], "purification_actions": []}
|
| 189 |
operator_state = operator_state or {}
|
| 190 |
status = str(scan.get("status", "idle")).upper()
|
| 191 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 192 |
checkpoint = str(operator_state.get("checkpoint", "pending")).replace("_", " ").title()
|
| 193 |
+
raw_findings = scan.get("findings")
|
| 194 |
+
if raw_findings is None:
|
| 195 |
+
default_finding = "No upload selected. Always-on scanner ready." if status == "IDLE" else "No findings."
|
| 196 |
+
raw_findings = [default_finding]
|
| 197 |
+
elif not raw_findings:
|
| 198 |
+
raw_findings = ["No findings." if status != "IDLE" else "No upload selected. Always-on scanner ready."]
|
| 199 |
+
findings = [_redact_scan_text(item) for item in raw_findings]
|
| 200 |
actions = [_redact_scan_text(item) for item in (scan.get("purification_actions") or [
|
| 201 |
"strip metadata before export",
|
| 202 |
"truncate PNG after IEND when needed",
|
|
|
|
| 225 |
operator_state: dict | None = None,
|
| 226 |
) -> str:
|
| 227 |
"""
|
| 228 |
+
Render the dashboard topbar with budget metrics, relay status, adult mode controls, and trust model information.
|
| 229 |
|
| 230 |
Parameters:
|
| 231 |
+
relay_status: Dictionary with relay rotation safety information.
|
| 232 |
+
scan: Dictionary with scan status and findings.
|
| 233 |
+
operator_state: Dictionary with operator checkpoint and provider state.
|
| 234 |
|
| 235 |
Returns:
|
| 236 |
+
HTML markup for the topbar and trust strip sections.
|
| 237 |
"""
|
| 238 |
summary = catalog_summary(adult_mode)
|
| 239 |
active = float(summary["active_b"])
|
|
|
|
| 284 |
|
| 285 |
|
| 286 |
def render_command_rail(active_section: str = "Forge") -> str:
|
| 287 |
+
"""
|
| 288 |
+
Render a contextual command rail for the selected section.
|
| 289 |
+
|
| 290 |
+
Returns:
|
| 291 |
+
str: An HTML string with the command rail displaying the section's title, description, and selection badge.
|
| 292 |
+
"""
|
| 293 |
section = escape(active_section)
|
| 294 |
hints = {
|
| 295 |
"Forge": ("Active Weave", "Prompt, judge, locate, generate, checkpoint."),
|
|
|
|
| 310 |
|
| 311 |
|
| 312 |
def render_workflow(run: GenerationRun | None = None, operator_state: dict | None = None) -> str:
|
| 313 |
+
"""
|
| 314 |
+
Render an SVG workflow diagram and console showing the generation pipeline.
|
| 315 |
+
|
| 316 |
+
The diagram visualizes a multi-stage workflow (seed prompt, refine, judge, locate, generate, video, checkpoint)
|
| 317 |
+
with nodes, edges, and status indicators. Node content and labels reflect the provided run and operator context.
|
| 318 |
+
|
| 319 |
+
Parameters:
|
| 320 |
+
run: GenerationRun object providing run-specific values for trust score, checkpoint ID, model labels,
|
| 321 |
+
video preset, and required actions. Defaults to mock values if None.
|
| 322 |
+
operator_state: Dict containing operator context including checkpoint status, provider state, recommendation,
|
| 323 |
+
and message. Defaults to an empty dict.
|
| 324 |
+
|
| 325 |
+
Returns:
|
| 326 |
+
str: HTML section containing SVG workflow graph, legend badges, and console cards with checkpoint,
|
| 327 |
+
action, model lane, and signal information.
|
| 328 |
+
"""
|
| 329 |
operator_state = operator_state or {}
|
| 330 |
score = run.checkpoint.trust_score if run else 0.82
|
| 331 |
checkpoint_id = run.checkpoint.checkpoint_id if run else "nw-dry-run"
|
|
|
|
| 433 |
|
| 434 |
|
| 435 |
def render_artifact_lane(run: GenerationRun | None = None, scan: dict | None = None, operator_state: dict | None = None) -> str:
|
| 436 |
+
"""
|
| 437 |
+
Render an artifact preview panel with generated output and status cards.
|
| 438 |
+
|
| 439 |
+
Displays the current artifact output (if available from a provider call), preview metadata
|
| 440 |
+
about checkpoint and export status, and a grid of artifact cards showing progress through
|
| 441 |
+
generation stages. Supports both dry-run and live provider output scenarios.
|
| 442 |
+
|
| 443 |
+
Parameters:
|
| 444 |
+
run (GenerationRun | None): Generation run with output image path, refined prompt, and continuity data.
|
| 445 |
+
If None, shows dry-run placeholder content.
|
| 446 |
+
scan (dict | None): Security scan state with status and export gate information.
|
| 447 |
+
Defaults to {"status": "idle", "export_gate": "pending"}.
|
| 448 |
+
operator_state (dict | None): Operator context including checkpoint status, provider state, and generated output.
|
| 449 |
+
Defaults to {}.
|
| 450 |
+
|
| 451 |
+
Returns:
|
| 452 |
+
str: HTML markup for the artifact preview section.
|
| 453 |
+
"""
|
| 454 |
scan = scan or {"status": "idle", "export_gate": "pending"}
|
| 455 |
operator_state = operator_state or {}
|
| 456 |
prompt_label = "Prompt proof"
|
|
|
|
| 537 |
"""
|
| 538 |
Render an operations panel with section-specific operation cards.
|
| 539 |
|
| 540 |
+
Generates an HTML section containing three operation cards tailored to the selected section,
|
| 541 |
+
with content drawn from run state, scan results, relay decisions, and operator context.
|
| 542 |
Invalid section names default to "Forge".
|
| 543 |
|
| 544 |
Parameters:
|
| 545 |
active_section (str): The section name determining which operation cards to display.
|
| 546 |
Valid sections are "Forge", "Wardrobe", "Lore", "Models", "Security", and "Runs".
|
| 547 |
adult_mode (bool): If True, scope label is "Private research scope"; otherwise "Public demo scope".
|
| 548 |
+
operator_state (dict | None): Operator context dict with "provider_state" and "message" keys.
|
| 549 |
|
| 550 |
Returns:
|
| 551 |
str: HTML string representing the operations panel section.
|
|
|
|
| 638 |
|
| 639 |
|
| 640 |
def _render_relay_panel(relay_status: dict | None = None) -> str:
|
| 641 |
+
"""
|
| 642 |
+
Render an HTML panel summarizing model relay decisions and pinned lanes.
|
| 643 |
+
|
| 644 |
+
Returns:
|
| 645 |
+
str: An HTML string containing the GMR panel with pinned core lanes, up to two decisions, and dedup metrics.
|
| 646 |
+
"""
|
| 647 |
relay_status = relay_status or {}
|
| 648 |
pinned = relay_status.get("pinned", {})
|
| 649 |
decisions = relay_status.get("decisions", [])
|
|
|
|
| 690 |
|
| 691 |
def render_provider_cards(relay_status: dict | None = None, adult_mode: bool = False) -> str:
|
| 692 |
"""
|
| 693 |
+
Generates an HTML panel displaying provider cards with operational status and licensing information.
|
| 694 |
+
|
| 695 |
+
Constructs cards for each relay-decided provider showing the lane, repository, provider name, license gate status, and health meter. Includes optional provider cards for off-by-default gateways. The panel header shows the current operational mode (private research or public demo).
|
| 696 |
|
| 697 |
Parameters:
|
| 698 |
+
relay_status (dict | None): Relay status containing provider decisions with quota impact and license gate data. If None, defaults to empty dict.
|
| 699 |
+
adult_mode (bool): If True, displays "PRIVATE RESEARCH" mode label; otherwise displays "PUBLIC DEMO SAFE".
|
| 700 |
|
| 701 |
Returns:
|
| 702 |
+
str: HTML section markup for the provider cards panel.
|
| 703 |
"""
|
| 704 |
relay_status = relay_status or {}
|
| 705 |
decisions = relay_status.get("decisions", [])
|
|
|
|
| 761 |
|
| 762 |
|
| 763 |
def _scan_status_tone(scan_status: str) -> str:
|
| 764 |
+
"""
|
| 765 |
+
Map a scan status to a display tone.
|
| 766 |
+
|
| 767 |
+
Returns:
|
| 768 |
+
str: "pass" if status is "pass", "warn" if status is "review" or "error", "muted" otherwise.
|
| 769 |
+
"""
|
| 770 |
if scan_status == "pass":
|
| 771 |
return "pass"
|
| 772 |
if scan_status in {"review", "error"}:
|
|
|
|
| 780 |
relay_status: dict | None = None,
|
| 781 |
operator_state: dict | None = None,
|
| 782 |
) -> str:
|
| 783 |
+
"""
|
| 784 |
+
Render an inspector sidebar with taste profile, material checklist, model stack, sponsor evidence, and ST3GG scan details.
|
| 785 |
+
|
| 786 |
+
Returns:
|
| 787 |
+
str: HTML markup for the inspector panel.
|
| 788 |
+
"""
|
| 789 |
if run:
|
| 790 |
checks = [
|
| 791 |
("Patent Leather", True),
|
|
|
|
| 989 |
operator_state: dict | None = None,
|
| 990 |
) -> dict[str, str]:
|
| 991 |
"""
|
| 992 |
+
Assemble all dashboard UI regions into a dictionary of HTML markup.
|
|
|
|
|
|
|
| 993 |
|
| 994 |
Parameters:
|
| 995 |
+
run: A generation run or None.
|
| 996 |
adult_mode: Whether to render adult-mode content.
|
| 997 |
+
scan: Scanner status and findings or None.
|
| 998 |
+
relay_status: Model relay configuration or None.
|
| 999 |
active_section: The currently active navigation section.
|
| 1000 |
+
operator_state: Operator control state or None.
|
| 1001 |
|
| 1002 |
Returns:
|
| 1003 |
+
dict[str, str]: HTML markup strings for each dashboard region.
|
| 1004 |
"""
|
| 1005 |
return {
|
| 1006 |
"topbar": render_topbar(adult_mode, relay_status, scan, operator_state),
|
src/nexus_visual_weaver/wardrobe.py
CHANGED
|
@@ -18,6 +18,17 @@ SLOT_BLUEPRINTS: list[tuple[str, str, str, str, str]] = [
|
|
| 18 |
|
| 19 |
|
| 20 |
def build_outfit_graph(prompt: str, adult_mode: bool = False, controls: dict | None = None) -> OutfitGraph:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
lowered = prompt.lower()
|
| 22 |
controls = controls or {}
|
| 23 |
locked_slots = set(controls.get("locked_slots") or [])
|
|
|
|
| 18 |
|
| 19 |
|
| 20 |
def build_outfit_graph(prompt: str, adult_mode: bool = False, controls: dict | None = None) -> OutfitGraph:
|
| 21 |
+
"""
|
| 22 |
+
Constructs an outfit graph with wardrobe slots configured by prompt analysis and optional controls.
|
| 23 |
+
|
| 24 |
+
Parameters:
|
| 25 |
+
prompt (str): Text to analyze for slot configuration and locking keywords.
|
| 26 |
+
adult_mode (bool): If True, marks upper_body and lower_body slots as adult-only. Defaults to False.
|
| 27 |
+
controls (dict | None): Optional configuration dict with keys: locked_slots, locate_focus, outerwear, upper_body, footwear, hardware, palette.
|
| 28 |
+
|
| 29 |
+
Returns:
|
| 30 |
+
OutfitGraph: Graph containing configured wardrobe slots and a computed score.
|
| 31 |
+
"""
|
| 32 |
lowered = prompt.lower()
|
| 33 |
controls = controls or {}
|
| 34 |
locked_slots = set(controls.get("locked_slots") or [])
|
tests/test_app_callbacks.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
| 1 |
import app
|
|
|
|
|
|
|
| 2 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 3 |
|
| 4 |
|
|
@@ -43,6 +45,21 @@ def test_checkpoint_seed_uses_only_last_8_chars() -> None:
|
|
| 43 |
assert result == int("11223344", 16) % 1_000_000
|
| 44 |
|
| 45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
# --- _wardrobe_summary tests ---
|
| 47 |
|
| 48 |
def test_wardrobe_summary_returns_string_with_slot_fields() -> None:
|
|
|
|
| 1 |
import app
|
| 2 |
+
import hashlib
|
| 3 |
+
from pathlib import Path
|
| 4 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 5 |
|
| 6 |
|
|
|
|
| 45 |
assert result == int("11223344", 16) % 1_000_000
|
| 46 |
|
| 47 |
|
| 48 |
+
def test_safe_file_hash_streams_digest_and_size() -> None:
|
| 49 |
+
payload = b"nexus-visual-weaver" * 1024
|
| 50 |
+
target = Path("outputs/test-hash-reference.bin")
|
| 51 |
+
target.parent.mkdir(parents=True, exist_ok=True)
|
| 52 |
+
target.write_bytes(payload)
|
| 53 |
+
|
| 54 |
+
try:
|
| 55 |
+
digest, size = app._safe_file_hash(str(target))
|
| 56 |
+
|
| 57 |
+
assert digest == hashlib.sha256(payload).hexdigest()
|
| 58 |
+
assert size == len(payload)
|
| 59 |
+
finally:
|
| 60 |
+
target.unlink(missing_ok=True)
|
| 61 |
+
|
| 62 |
+
|
| 63 |
# --- _wardrobe_summary tests ---
|
| 64 |
|
| 65 |
def test_wardrobe_summary_returns_string_with_slot_fields() -> None:
|
tests/test_command_center.py
CHANGED
|
@@ -74,6 +74,9 @@ def test_public_stack_uses_flux_9b_and_excludes_offellia() -> None:
|
|
| 74 |
|
| 75 |
|
| 76 |
def test_private_catalog_keeps_stronger_research_models_available() -> None:
|
|
|
|
|
|
|
|
|
|
| 77 |
private_models, _ = filter_catalog(True)
|
| 78 |
repo_ids = {model.repo_id for model in private_models}
|
| 79 |
|
|
@@ -259,6 +262,14 @@ def test_render_trust_strip_pass_scan_shows_clear_export() -> None:
|
|
| 259 |
assert "all checks passed" in html
|
| 260 |
|
| 261 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
def test_render_trust_strip_review_scan_shows_blocked_export() -> None:
|
| 263 |
scan = {"status": "review", "export_gate": "blocked", "findings": ["trailing data after IEND"], "purification_actions": ["truncate PNG"]}
|
| 264 |
html = render_trust_strip(scan=scan)
|
|
|
|
| 74 |
|
| 75 |
|
| 76 |
def test_private_catalog_keeps_stronger_research_models_available() -> None:
|
| 77 |
+
"""
|
| 78 |
+
Verify that private catalog mode includes OFFELLIA and other research models alongside standard models.
|
| 79 |
+
"""
|
| 80 |
private_models, _ = filter_catalog(True)
|
| 81 |
repo_ids = {model.repo_id for model in private_models}
|
| 82 |
|
|
|
|
| 262 |
assert "all checks passed" in html
|
| 263 |
|
| 264 |
|
| 265 |
+
def test_render_trust_strip_clean_empty_findings_shows_no_findings() -> None:
|
| 266 |
+
scan = {"status": "pass", "export_gate": "clear", "findings": [], "purification_actions": []}
|
| 267 |
+
html = render_trust_strip(scan=scan)
|
| 268 |
+
|
| 269 |
+
assert "No findings." in html
|
| 270 |
+
assert "No upload selected. Always-on scanner ready." not in html
|
| 271 |
+
|
| 272 |
+
|
| 273 |
def test_render_trust_strip_review_scan_shows_blocked_export() -> None:
|
| 274 |
scan = {"status": "review", "export_gate": "blocked", "findings": ["trailing data after IEND"], "purification_actions": ["truncate PNG"]}
|
| 275 |
html = render_trust_strip(scan=scan)
|
tests/test_exporter.py
CHANGED
|
@@ -6,6 +6,15 @@ from nexus_visual_weaver.planner import build_command_center_run
|
|
| 6 |
|
| 7 |
|
| 8 |
def _make_base_state(**overrides):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
state = {
|
| 10 |
"provider_state": "export_ready",
|
| 11 |
"checkpoint": "approved",
|
|
@@ -189,6 +198,12 @@ def test_export_root_falls_back_to_outputs_when_no_env(monkeypatch) -> None:
|
|
| 189 |
original_exists = Path.exists
|
| 190 |
|
| 191 |
def patched_exists(self):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
if str(self) == "/data":
|
| 193 |
return False
|
| 194 |
return original_exists(self)
|
|
@@ -271,6 +286,25 @@ def test_export_packet_redacts_raw_payload_paths_and_secret_like_values(monkeypa
|
|
| 271 |
assert payload["provider_states"]["minicpm"] == "failed"
|
| 272 |
|
| 273 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
def test_export_packet_records_st3gg_override_reason(monkeypatch) -> None:
|
| 275 |
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 276 |
run = build_command_center_run("gothic patent leather platform boots")
|
|
|
|
| 6 |
|
| 7 |
|
| 8 |
def _make_base_state(**overrides):
|
| 9 |
+
"""
|
| 10 |
+
Create a test operator state payload with optional field overrides.
|
| 11 |
+
|
| 12 |
+
Parameters:
|
| 13 |
+
**overrides: Fields to override in the base state dictionary.
|
| 14 |
+
|
| 15 |
+
Returns:
|
| 16 |
+
dict: A dictionary containing operator state data for export packet testing.
|
| 17 |
+
"""
|
| 18 |
state = {
|
| 19 |
"provider_state": "export_ready",
|
| 20 |
"checkpoint": "approved",
|
|
|
|
| 198 |
original_exists = Path.exists
|
| 199 |
|
| 200 |
def patched_exists(self):
|
| 201 |
+
"""
|
| 202 |
+
Custom exists check that treats "/data" as non-existent.
|
| 203 |
+
|
| 204 |
+
Returns:
|
| 205 |
+
False if the path is "/data", otherwise the original exists check result.
|
| 206 |
+
"""
|
| 207 |
if str(self) == "/data":
|
| 208 |
return False
|
| 209 |
return original_exists(self)
|
|
|
|
| 286 |
assert payload["provider_states"]["minicpm"] == "failed"
|
| 287 |
|
| 288 |
|
| 289 |
+
def test_export_packet_redacts_windows_backslash_paths_on_linux(monkeypatch) -> None:
|
| 290 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 291 |
+
run = build_command_center_run("redaction brief")
|
| 292 |
+
scan = {
|
| 293 |
+
"status": "review",
|
| 294 |
+
"export_gate": "blocked",
|
| 295 |
+
"findings": [r"raw hidden bytes at C:\Users\speci.000\Downloads\thing.png"],
|
| 296 |
+
"purification_actions": [],
|
| 297 |
+
}
|
| 298 |
+
state = _make_base_state()
|
| 299 |
+
|
| 300 |
+
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 301 |
+
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
| 302 |
+
serialized = json.dumps(payload)
|
| 303 |
+
|
| 304 |
+
assert r"C:\Users\speci.000\Downloads" not in serialized
|
| 305 |
+
assert "[local_path]/thing.png" in serialized
|
| 306 |
+
|
| 307 |
+
|
| 308 |
def test_export_packet_records_st3gg_override_reason(monkeypatch) -> None:
|
| 309 |
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 310 |
run = build_command_center_run("gothic patent leather platform boots")
|
tests/test_lora_adapter.py
CHANGED
|
@@ -9,9 +9,15 @@ class FakeLoraPipe:
|
|
| 9 |
self.unloaded = False
|
| 10 |
|
| 11 |
def load_lora_weights(self, repo_id: str, **kwargs) -> None:
|
|
|
|
|
|
|
|
|
|
| 12 |
self.loaded.append((repo_id, kwargs))
|
| 13 |
|
| 14 |
def set_adapters(self, adapter_names: list[str], adapter_weights: list[float]) -> None:
|
|
|
|
|
|
|
|
|
|
| 15 |
self.adapters = (adapter_names, adapter_weights)
|
| 16 |
|
| 17 |
def unload_lora_weights(self) -> None:
|
|
|
|
| 9 |
self.unloaded = False
|
| 10 |
|
| 11 |
def load_lora_weights(self, repo_id: str, **kwargs) -> None:
|
| 12 |
+
"""
|
| 13 |
+
Record a call to load LoRA weights for later assertion in tests.
|
| 14 |
+
"""
|
| 15 |
self.loaded.append((repo_id, kwargs))
|
| 16 |
|
| 17 |
def set_adapters(self, adapter_names: list[str], adapter_weights: list[float]) -> None:
|
| 18 |
+
"""
|
| 19 |
+
Store adapter names and their corresponding weights for later inspection.
|
| 20 |
+
"""
|
| 21 |
self.adapters = (adapter_names, adapter_weights)
|
| 22 |
|
| 23 |
def unload_lora_weights(self) -> None:
|