Spaces:
Runtime error
Runtime error
Deploy PR5 scan gate hardening 1cca102
Browse files- README.md +5 -3
- app.py +42 -17
- docs/HANDOFF_FINAL_HACKATHON.md +2 -2
- src/nexus_visual_weaver/exporter.py +52 -9
- src/nexus_visual_weaver/provider_runtime.py +4 -0
- src/nexus_visual_weaver/render.py +37 -12
- tests/test_app_callbacks.py +148 -1
- tests/test_command_center.py +298 -2
- tests/test_exporter.py +163 -6
- tests/test_model_relay.py +76 -0
- tests/test_provider_runtime.py +312 -1
README.md
CHANGED
|
@@ -92,17 +92,18 @@ Public demo mode excludes private, commercial-uncleared, and research-only helpe
|
|
| 92 |
| <=32B models | Public stack is 11.42B: FLUX.2 Klein 4B + LocateAnything 3.83B + MiniCPM-V 1.30B + Nemotron Parse 0.94B + MiniCPM5 1.08B + FunctionGemma 0.27B. |
|
| 93 |
| Off Brand | Custom command-center UI, dense inspector, workflow graph, wardrobe/lore drawer, and provider cards. |
|
| 94 |
| Best Agent | Multi-step prompt, generation, scan, judge, checkpoint, export workflow. |
|
| 95 |
-
| OpenBMB | Claimed only when MiniCPM-V returns
|
| 96 |
-
| NVIDIA | Claimed only when Nemotron returns
|
| 97 |
| OpenAI Codex | Development branch and PR include Codex-authored implementation commits. |
|
| 98 |
| Demo / social | Add final links here before submission: `DEMO_VIDEO_URL` and `SOCIAL_POST_URL`. |
|
| 99 |
|
| 100 |
-
Tiny Titan can be claimed
|
| 101 |
|
| 102 |
## Local Setup
|
| 103 |
|
| 104 |
```powershell
|
| 105 |
python -m pip install -r requirements.txt
|
|
|
|
| 106 |
python app.py
|
| 107 |
```
|
| 108 |
|
|
@@ -112,6 +113,7 @@ The app reads `NEXUS_PORT` or `PORT` when present, otherwise it launches on `786
|
|
| 112 |
|
| 113 |
```powershell
|
| 114 |
python -m compileall app.py src tests
|
|
|
|
| 115 |
$env:PYTEST_DISABLE_PLUGIN_AUTOLOAD='1'
|
| 116 |
python -m pytest -q tests -p no:cacheprovider --basetemp=C:\tmp\pytest-nvw-full
|
| 117 |
```
|
|
|
|
| 92 |
| <=32B models | Public stack is 11.42B: FLUX.2 Klein 4B + LocateAnything 3.83B + MiniCPM-V 1.30B + Nemotron Parse 0.94B + MiniCPM5 1.08B + FunctionGemma 0.27B. |
|
| 93 |
| Off Brand | Custom command-center UI, dense inspector, workflow graph, wardrobe/lore drawer, and provider cards. |
|
| 94 |
| Best Agent | Multi-step prompt, generation, scan, judge, checkpoint, export workflow. |
|
| 95 |
+
| OpenBMB | Claimed only when MiniCPM-V returns success status in an export packet. |
|
| 96 |
+
| NVIDIA | Claimed only when Nemotron returns success status in an export packet. LocateAnything remains visible but is not the Nemotron claim by itself. |
|
| 97 |
| OpenAI Codex | Development branch and PR include Codex-authored implementation commits. |
|
| 98 |
| Demo / social | Add final links here before submission: `DEMO_VIDEO_URL` and `SOCIAL_POST_URL`. |
|
| 99 |
|
| 100 |
+
Tiny Titan can be claimed only from a successful public-demo export packet because the active public models are each <=4B. The stronger FLUX.2 Klein 9B and OFFELLIA/Gemma stack remains private research only.
|
| 101 |
|
| 102 |
## Local Setup
|
| 103 |
|
| 104 |
```powershell
|
| 105 |
python -m pip install -r requirements.txt
|
| 106 |
+
$env:NEXUS_DISABLE_REAL_HF='1'
|
| 107 |
python app.py
|
| 108 |
```
|
| 109 |
|
|
|
|
| 113 |
|
| 114 |
```powershell
|
| 115 |
python -m compileall app.py src tests
|
| 116 |
+
$env:NEXUS_DISABLE_REAL_HF='1'
|
| 117 |
$env:PYTEST_DISABLE_PLUGIN_AUTOLOAD='1'
|
| 118 |
python -m pytest -q tests -p no:cacheprovider --basetemp=C:\tmp\pytest-nvw-full
|
| 119 |
```
|
app.py
CHANGED
|
@@ -77,6 +77,20 @@ def _file_path(uploaded: Any) -> str | None:
|
|
| 77 |
return str(path) if path else None
|
| 78 |
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
| 81 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 82 |
if not suffix:
|
|
@@ -133,7 +147,7 @@ def run_weave(
|
|
| 133 |
)
|
| 134 |
reference_scan = scan_file(_file_path(upload))
|
| 135 |
generation = generate_flux_image(run.refined_prompt.refined, seed=_checkpoint_seed(run.checkpoint.checkpoint_id))
|
| 136 |
-
generated_scan = scan_file(generation.output_path) if generation.output_path else
|
| 137 |
minicpm = judge_with_minicpm(
|
| 138 |
prompt=run.refined_prompt.refined,
|
| 139 |
image_path=generation.output_path,
|
|
@@ -278,25 +292,32 @@ def scan_reference(
|
|
| 278 |
active_section: str,
|
| 279 |
operator_state: dict[str, Any] | None,
|
| 280 |
) -> tuple[Any, ...]:
|
| 281 |
-
|
|
|
|
|
|
|
|
|
|
| 282 |
minicpm = None
|
| 283 |
-
if run is not None:
|
| 284 |
-
generated_path = ((operator_state or {}).get("generation") or {}).get("output_path")
|
| 285 |
minicpm = judge_with_minicpm(
|
| 286 |
prompt=getattr(getattr(run, "refined_prompt", None), "refined", DEFAULT_PROMPT),
|
| 287 |
-
image_path=
|
| 288 |
-
scan=
|
| 289 |
wardrobe_summary=_wardrobe_summary(run),
|
| 290 |
)
|
| 291 |
next_state = {
|
| 292 |
-
**
|
| 293 |
**({"reference_judge": minicpm.to_dict()} if minicpm else {}),
|
| 294 |
-
"reference_scan":
|
| 295 |
-
"
|
| 296 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 297 |
}
|
| 298 |
-
rendered = _render_stateful(run, adult_mode,
|
| 299 |
-
return (*rendered,
|
| 300 |
|
| 301 |
|
| 302 |
def approve_checkpoint(
|
|
@@ -306,12 +327,13 @@ def approve_checkpoint(
|
|
| 306 |
active_section: str,
|
| 307 |
operator_state: dict[str, Any] | None,
|
| 308 |
) -> tuple[Any, ...]:
|
| 309 |
-
|
|
|
|
| 310 |
if run is None:
|
| 311 |
next_state = {**_default_operator_state(), "provider_state": "blocked", "message": "No run exists yet. Run Active Weave first."}
|
| 312 |
-
elif not (
|
| 313 |
next_state = {
|
| 314 |
-
**
|
| 315 |
"provider_state": "blocked",
|
| 316 |
"checkpoint": "pending",
|
| 317 |
"message": "Checkpoint blocked: no generated artifact exists yet.",
|
|
@@ -319,9 +341,10 @@ def approve_checkpoint(
|
|
| 319 |
else:
|
| 320 |
export_state = scan.get("export_gate", "pending")
|
| 321 |
next_state = {
|
| 322 |
-
**
|
| 323 |
"provider_state": "export_ready" if export_state == "clear" else "checkpointed",
|
| 324 |
"checkpoint": "approved",
|
|
|
|
| 325 |
"export": export_state,
|
| 326 |
"message": "Checkpoint approved. Export is ready after clear ST3GG scan." if export_state == "clear" else "Checkpoint approved, but export still waits on ST3GG review.",
|
| 327 |
}
|
|
@@ -335,12 +358,14 @@ def export_packet(
|
|
| 335 |
active_section: str,
|
| 336 |
operator_state: dict[str, Any] | None,
|
| 337 |
) -> tuple[Any, ...]:
|
| 338 |
-
scan = scan or scan_file(None)
|
| 339 |
state = operator_state or _default_operator_state()
|
|
|
|
| 340 |
if run is None:
|
| 341 |
next_state = {**state, "provider_state": "blocked", "export": "blocked", "message": "Export blocked: no active run packet exists."}
|
| 342 |
elif state.get("checkpoint") != "approved":
|
| 343 |
next_state = {**state, "provider_state": "blocked", "export": "blocked", "message": "Export blocked: human checkpoint has not been approved."}
|
|
|
|
|
|
|
| 344 |
elif scan.get("export_gate") != "clear":
|
| 345 |
next_state = {**state, "provider_state": "blocked", "export": scan.get("export_gate", "blocked"), "message": "Export blocked: ST3GG gate is not clear."}
|
| 346 |
else:
|
|
|
|
| 77 |
return str(path) if path else None
|
| 78 |
|
| 79 |
|
| 80 |
+
def _generated_output_path(operator_state: dict[str, Any] | None) -> str | None:
|
| 81 |
+
generation = (operator_state or {}).get("generation") or {}
|
| 82 |
+
output_path = generation.get("output_path")
|
| 83 |
+
return str(output_path) if output_path else None
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def _authoritative_generated_scan(operator_state: dict[str, Any] | None) -> dict[str, Any]:
|
| 87 |
+
output_path = _generated_output_path(operator_state)
|
| 88 |
+
if output_path:
|
| 89 |
+
return scan_file(output_path)
|
| 90 |
+
stored_scan = (operator_state or {}).get("generated_scan")
|
| 91 |
+
return stored_scan if isinstance(stored_scan, dict) else scan_file(None)
|
| 92 |
+
|
| 93 |
+
|
| 94 |
def _checkpoint_seed(checkpoint_id: str) -> int:
|
| 95 |
suffix = "".join(char for char in checkpoint_id[-8:] if char in "0123456789abcdefABCDEF")
|
| 96 |
if not suffix:
|
|
|
|
| 147 |
)
|
| 148 |
reference_scan = scan_file(_file_path(upload))
|
| 149 |
generation = generate_flux_image(run.refined_prompt.refined, seed=_checkpoint_seed(run.checkpoint.checkpoint_id))
|
| 150 |
+
generated_scan = scan_file(generation.output_path) if generation.output_path else scan_file(None)
|
| 151 |
minicpm = judge_with_minicpm(
|
| 152 |
prompt=run.refined_prompt.refined,
|
| 153 |
image_path=generation.output_path,
|
|
|
|
| 292 |
active_section: str,
|
| 293 |
operator_state: dict[str, Any] | None,
|
| 294 |
) -> tuple[Any, ...]:
|
| 295 |
+
state = operator_state or _default_operator_state()
|
| 296 |
+
reference_path = _file_path(upload)
|
| 297 |
+
reference_scan = scan_file(reference_path)
|
| 298 |
+
generated_scan = _authoritative_generated_scan(state)
|
| 299 |
minicpm = None
|
| 300 |
+
if run is not None and reference_path:
|
|
|
|
| 301 |
minicpm = judge_with_minicpm(
|
| 302 |
prompt=getattr(getattr(run, "refined_prompt", None), "refined", DEFAULT_PROMPT),
|
| 303 |
+
image_path=reference_path,
|
| 304 |
+
scan=reference_scan,
|
| 305 |
wardrobe_summary=_wardrobe_summary(run),
|
| 306 |
)
|
| 307 |
next_state = {
|
| 308 |
+
**state,
|
| 309 |
**({"reference_judge": minicpm.to_dict()} if minicpm else {}),
|
| 310 |
+
"reference_scan": reference_scan,
|
| 311 |
+
"reference_export_gate": reference_scan.get("export_gate", "pending"),
|
| 312 |
+
"export": state.get("export", generated_scan.get("export_gate", "pending")),
|
| 313 |
+
"message": (
|
| 314 |
+
"Reference scan complete. Generated artifact export gate is unchanged."
|
| 315 |
+
if reference_scan.get("export_gate") == "clear"
|
| 316 |
+
else "Reference scan requires review. Generated artifact export gate is unchanged."
|
| 317 |
+
),
|
| 318 |
}
|
| 319 |
+
rendered = _render_stateful(run, adult_mode, generated_scan, active_section, next_state)
|
| 320 |
+
return (*rendered, generated_scan)
|
| 321 |
|
| 322 |
|
| 323 |
def approve_checkpoint(
|
|
|
|
| 327 |
active_section: str,
|
| 328 |
operator_state: dict[str, Any] | None,
|
| 329 |
) -> tuple[Any, ...]:
|
| 330 |
+
state = operator_state or _default_operator_state()
|
| 331 |
+
scan = _authoritative_generated_scan(state)
|
| 332 |
if run is None:
|
| 333 |
next_state = {**_default_operator_state(), "provider_state": "blocked", "message": "No run exists yet. Run Active Weave first."}
|
| 334 |
+
elif not _generated_output_path(state):
|
| 335 |
next_state = {
|
| 336 |
+
**state,
|
| 337 |
"provider_state": "blocked",
|
| 338 |
"checkpoint": "pending",
|
| 339 |
"message": "Checkpoint blocked: no generated artifact exists yet.",
|
|
|
|
| 341 |
else:
|
| 342 |
export_state = scan.get("export_gate", "pending")
|
| 343 |
next_state = {
|
| 344 |
+
**state,
|
| 345 |
"provider_state": "export_ready" if export_state == "clear" else "checkpointed",
|
| 346 |
"checkpoint": "approved",
|
| 347 |
+
"generated_scan": scan,
|
| 348 |
"export": export_state,
|
| 349 |
"message": "Checkpoint approved. Export is ready after clear ST3GG scan." if export_state == "clear" else "Checkpoint approved, but export still waits on ST3GG review.",
|
| 350 |
}
|
|
|
|
| 358 |
active_section: str,
|
| 359 |
operator_state: dict[str, Any] | None,
|
| 360 |
) -> tuple[Any, ...]:
|
|
|
|
| 361 |
state = operator_state or _default_operator_state()
|
| 362 |
+
scan = _authoritative_generated_scan(state)
|
| 363 |
if run is None:
|
| 364 |
next_state = {**state, "provider_state": "blocked", "export": "blocked", "message": "Export blocked: no active run packet exists."}
|
| 365 |
elif state.get("checkpoint") != "approved":
|
| 366 |
next_state = {**state, "provider_state": "blocked", "export": "blocked", "message": "Export blocked: human checkpoint has not been approved."}
|
| 367 |
+
elif not _generated_output_path(state):
|
| 368 |
+
next_state = {**state, "provider_state": "blocked", "export": "blocked", "message": "Export blocked: no generated artifact exists."}
|
| 369 |
elif scan.get("export_gate") != "clear":
|
| 370 |
next_state = {**state, "provider_state": "blocked", "export": scan.get("export_gate", "blocked"), "message": "Export blocked: ST3GG gate is not clear."}
|
| 371 |
else:
|
docs/HANDOFF_FINAL_HACKATHON.md
CHANGED
|
@@ -26,8 +26,8 @@ Do not paste these into chat, commits, logs, or export packets.
|
|
| 26 |
|
| 27 |
```powershell
|
| 28 |
python -m compileall app.py src tests
|
| 29 |
-
python -c "import app; print('app import ok')"
|
| 30 |
$env:NEXUS_DISABLE_REAL_HF='1'
|
|
|
|
| 31 |
$env:PYTEST_DISABLE_PLUGIN_AUTOLOAD='1'
|
| 32 |
python -m pytest -q tests -p no:cacheprovider
|
| 33 |
hf auth whoami --format json
|
|
@@ -51,7 +51,7 @@ Avoid pytest `--basetemp=C:\tmp` in this Windows sandbox if `tmp_path` fixtures
|
|
| 51 |
- OpenBMB prize claim requires `minicpm_judge.status == "success"` in an export packet.
|
| 52 |
- NVIDIA prize claim requires `nemotron_evidence.status == "success"` in an export packet.
|
| 53 |
- LocateAnything supports the grounding story but does not replace Nemotron for the NVIDIA prize.
|
| 54 |
-
- Tiny Titan can be claimed
|
| 55 |
- FLUX.2 Klein 9B and OFFELLIA/Gemma remain private research options only.
|
| 56 |
- Modal is not claimed unless a real Modal job runs and is documented.
|
| 57 |
|
|
|
|
| 26 |
|
| 27 |
```powershell
|
| 28 |
python -m compileall app.py src tests
|
|
|
|
| 29 |
$env:NEXUS_DISABLE_REAL_HF='1'
|
| 30 |
+
python -c "import app; print('app import ok')"
|
| 31 |
$env:PYTEST_DISABLE_PLUGIN_AUTOLOAD='1'
|
| 32 |
python -m pytest -q tests -p no:cacheprovider
|
| 33 |
hf auth whoami --format json
|
|
|
|
| 51 |
- OpenBMB prize claim requires `minicpm_judge.status == "success"` in an export packet.
|
| 52 |
- NVIDIA prize claim requires `nemotron_evidence.status == "success"` in an export packet.
|
| 53 |
- LocateAnything supports the grounding story but does not replace Nemotron for the NVIDIA prize.
|
| 54 |
+
- Tiny Titan can be claimed only from a successful public-demo export packet because each active public model is <=4B.
|
| 55 |
- FLUX.2 Klein 9B and OFFELLIA/Gemma remain private research options only.
|
| 56 |
- Modal is not claimed unless a real Modal job runs and is documented.
|
| 57 |
|
src/nexus_visual_weaver/exporter.py
CHANGED
|
@@ -10,6 +10,43 @@ from typing import Any
|
|
| 10 |
|
| 11 |
from .catalog import active_stack, parameter_budget
|
| 12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
def export_root() -> Path:
|
| 15 |
requested = os.environ.get("NEXUS_EXPORT_DIR")
|
|
@@ -18,14 +55,15 @@ def export_root() -> Path:
|
|
| 18 |
candidates.append(Path("/data/nexus_visual_weaver/exports"))
|
| 19 |
candidates.append(Path("outputs/exports"))
|
| 20 |
for candidate in candidates:
|
| 21 |
-
|
|
|
|
| 22 |
continue
|
| 23 |
try:
|
| 24 |
-
|
| 25 |
-
return
|
| 26 |
except OSError:
|
| 27 |
continue
|
| 28 |
-
fallback =
|
| 29 |
fallback.mkdir(parents=True, exist_ok=True)
|
| 30 |
return fallback
|
| 31 |
|
|
@@ -38,17 +76,22 @@ def write_export_packet(
|
|
| 38 |
adult_mode: bool,
|
| 39 |
) -> dict[str, Any]:
|
| 40 |
run_id = getattr(getattr(run, "checkpoint", None), "checkpoint_id", f"nw-{int(time.time())}")
|
| 41 |
-
|
|
|
|
|
|
|
| 42 |
generation = dict(operator_state.get("generation") or {})
|
| 43 |
generation.pop("hf_token_present", None)
|
|
|
|
|
|
|
|
|
|
| 44 |
packet = {
|
| 45 |
"schema": "nexus_visual_weaver.export_packet.v1",
|
| 46 |
"run_id": run_id,
|
| 47 |
"created_at_epoch": int(time.time()),
|
| 48 |
-
"adult_mode":
|
| 49 |
"prompt": getattr(getattr(run, "request", None), "prompt", ""),
|
| 50 |
"refined_prompt": getattr(getattr(run, "refined_prompt", None), "refined", ""),
|
| 51 |
-
"artifact":
|
| 52 |
"generation": generation,
|
| 53 |
"st3gg_scan": scan,
|
| 54 |
"minicpm_judge": operator_state.get("minicpm_judge") or {},
|
|
@@ -68,9 +111,9 @@ def write_export_packet(
|
|
| 68 |
}
|
| 69 |
for model in stack
|
| 70 |
],
|
| 71 |
-
"parameter_budget":
|
| 72 |
"hackathon_claims": {
|
| 73 |
-
"build_small_32b":
|
| 74 |
"gradio_space": True,
|
| 75 |
"off_brand_custom_ui": True,
|
| 76 |
"openbmb_lane": (operator_state.get("minicpm_judge") or {}).get("status") == "success",
|
|
|
|
| 10 |
|
| 11 |
from .catalog import active_stack, parameter_budget
|
| 12 |
|
| 13 |
+
REPO_ROOT = Path(__file__).resolve().parents[2]
|
| 14 |
+
ALLOWED_LOCAL_EXPORT_ROOTS = (
|
| 15 |
+
REPO_ROOT / "outputs",
|
| 16 |
+
REPO_ROOT / ".pi",
|
| 17 |
+
REPO_ROOT / ".codex",
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def _is_within(path: Path, root: Path) -> bool:
|
| 22 |
+
try:
|
| 23 |
+
path.relative_to(root)
|
| 24 |
+
return True
|
| 25 |
+
except ValueError:
|
| 26 |
+
return False
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _safe_export_candidate(candidate: Path) -> Path | None:
|
| 30 |
+
if not candidate.is_absolute():
|
| 31 |
+
candidate = REPO_ROOT / candidate
|
| 32 |
+
resolved = candidate.resolve(strict=False)
|
| 33 |
+
src_root = (REPO_ROOT / "src").resolve(strict=False)
|
| 34 |
+
if resolved == src_root or _is_within(resolved, src_root):
|
| 35 |
+
return None
|
| 36 |
+
|
| 37 |
+
allowed_roots = [root.resolve(strict=False) for root in ALLOWED_LOCAL_EXPORT_ROOTS]
|
| 38 |
+
if Path("/data").exists():
|
| 39 |
+
allowed_roots.append(Path("/data/nexus_visual_weaver").resolve(strict=False))
|
| 40 |
+
if any(resolved == root or _is_within(resolved, root) for root in allowed_roots):
|
| 41 |
+
return resolved
|
| 42 |
+
return None
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def _artifact_name(output_path: Any) -> str | None:
|
| 46 |
+
if not output_path:
|
| 47 |
+
return None
|
| 48 |
+
return Path(str(output_path)).name
|
| 49 |
+
|
| 50 |
|
| 51 |
def export_root() -> Path:
|
| 52 |
requested = os.environ.get("NEXUS_EXPORT_DIR")
|
|
|
|
| 55 |
candidates.append(Path("/data/nexus_visual_weaver/exports"))
|
| 56 |
candidates.append(Path("outputs/exports"))
|
| 57 |
for candidate in candidates:
|
| 58 |
+
safe_candidate = _safe_export_candidate(candidate)
|
| 59 |
+
if safe_candidate is None:
|
| 60 |
continue
|
| 61 |
try:
|
| 62 |
+
safe_candidate.mkdir(parents=True, exist_ok=True)
|
| 63 |
+
return safe_candidate
|
| 64 |
except OSError:
|
| 65 |
continue
|
| 66 |
+
fallback = (REPO_ROOT / "outputs/exports").resolve(strict=False)
|
| 67 |
fallback.mkdir(parents=True, exist_ok=True)
|
| 68 |
return fallback
|
| 69 |
|
|
|
|
| 76 |
adult_mode: bool,
|
| 77 |
) -> dict[str, Any]:
|
| 78 |
run_id = getattr(getattr(run, "checkpoint", None), "checkpoint_id", f"nw-{int(time.time())}")
|
| 79 |
+
run_adult_mode = bool(getattr(getattr(run, "request", None), "adult_mode", adult_mode))
|
| 80 |
+
stack = list(getattr(run, "model_stack", None) or active_stack(run_adult_mode))
|
| 81 |
+
budget = parameter_budget(stack)
|
| 82 |
generation = dict(operator_state.get("generation") or {})
|
| 83 |
generation.pop("hf_token_present", None)
|
| 84 |
+
artifact = _artifact_name(generation.get("output_path"))
|
| 85 |
+
if "output_path" in generation:
|
| 86 |
+
generation["output_path"] = artifact
|
| 87 |
packet = {
|
| 88 |
"schema": "nexus_visual_weaver.export_packet.v1",
|
| 89 |
"run_id": run_id,
|
| 90 |
"created_at_epoch": int(time.time()),
|
| 91 |
+
"adult_mode": run_adult_mode,
|
| 92 |
"prompt": getattr(getattr(run, "request", None), "prompt", ""),
|
| 93 |
"refined_prompt": getattr(getattr(run, "refined_prompt", None), "refined", ""),
|
| 94 |
+
"artifact": artifact,
|
| 95 |
"generation": generation,
|
| 96 |
"st3gg_scan": scan,
|
| 97 |
"minicpm_judge": operator_state.get("minicpm_judge") or {},
|
|
|
|
| 111 |
}
|
| 112 |
for model in stack
|
| 113 |
],
|
| 114 |
+
"parameter_budget": budget,
|
| 115 |
"hackathon_claims": {
|
| 116 |
+
"build_small_32b": budget["status"] == "pass",
|
| 117 |
"gradio_space": True,
|
| 118 |
"off_brand_custom_ui": True,
|
| 119 |
"openbmb_lane": (operator_state.get("minicpm_judge") or {}).get("status") == "success",
|
src/nexus_visual_weaver/provider_runtime.py
CHANGED
|
@@ -7,6 +7,7 @@ import json
|
|
| 7 |
import os
|
| 8 |
import time
|
| 9 |
import urllib.error
|
|
|
|
| 10 |
import urllib.request
|
| 11 |
from dataclasses import asdict, dataclass
|
| 12 |
from pathlib import Path
|
|
@@ -56,6 +57,9 @@ def _image_data_url(path: str | None) -> str | None:
|
|
| 56 |
|
| 57 |
|
| 58 |
def _post_json(url: str, token: str, payload: dict[str, Any], timeout: float) -> dict[str, Any]:
|
|
|
|
|
|
|
|
|
|
| 59 |
body = json.dumps(payload).encode("utf-8")
|
| 60 |
request = urllib.request.Request(
|
| 61 |
url,
|
|
|
|
| 7 |
import os
|
| 8 |
import time
|
| 9 |
import urllib.error
|
| 10 |
+
import urllib.parse
|
| 11 |
import urllib.request
|
| 12 |
from dataclasses import asdict, dataclass
|
| 13 |
from pathlib import Path
|
|
|
|
| 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}.")
|
| 63 |
body = json.dumps(payload).encode("utf-8")
|
| 64 |
request = urllib.request.Request(
|
| 65 |
url,
|
src/nexus_visual_weaver/render.py
CHANGED
|
@@ -48,6 +48,31 @@ def _env_configured(*names: str) -> bool:
|
|
| 48 |
return any(bool(os.environ.get(name)) for name in names)
|
| 49 |
|
| 50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
def _space_runtime_status() -> dict[str, str]:
|
| 52 |
space_id = os.environ.get("SPACE_ID") or os.environ.get("HF_SPACE_ID") or "local-preview"
|
| 53 |
hardware = os.environ.get("SPACE_HARDWARE") or os.environ.get("NEXUS_SPACE_HARDWARE") or "ZeroGPU"
|
|
@@ -55,9 +80,9 @@ def _space_runtime_status() -> dict[str, str]:
|
|
| 55 |
configured = []
|
| 56 |
if _env_configured("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN"):
|
| 57 |
configured.append("HF")
|
| 58 |
-
if
|
| 59 |
configured.append("MiniCPM")
|
| 60 |
-
if
|
| 61 |
configured.append("Nemotron")
|
| 62 |
if _env_configured("FAL_KEY", "NETLIFY_AUTH_TOKEN", "OPENAI_API_KEY", "MODAL_TOKEN_ID"):
|
| 63 |
configured.append("optional")
|
|
@@ -106,12 +131,12 @@ def render_trust_strip(scan: dict | None = None, operator_state: dict | None = N
|
|
| 106 |
status = str(scan.get("status", "idle")).upper()
|
| 107 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 108 |
checkpoint = str(operator_state.get("checkpoint", "pending")).replace("_", " ").title()
|
| 109 |
-
findings = scan.get("findings") or ["No upload selected. Always-on scanner ready."]
|
| 110 |
-
actions = scan.get("purification_actions") or [
|
| 111 |
"strip metadata before export",
|
| 112 |
"truncate PNG after IEND when needed",
|
| 113 |
"run LSB statistical review",
|
| 114 |
-
]
|
| 115 |
tone = _scan_status_tone(str(scan.get("status", "idle")))
|
| 116 |
return f"""
|
| 117 |
<section class="nw-trust-strip">
|
|
@@ -460,7 +485,7 @@ def render_operations_panel(
|
|
| 460 |
("ST3GG state", f"{scan_status} / export {export_gate}"),
|
| 461 |
(
|
| 462 |
"Findings",
|
| 463 |
-
"; ".join(
|
| 464 |
or ("No findings." if scan_status != "IDLE" else "No upload selected."),
|
| 465 |
),
|
| 466 |
("Public export", "Consent, provenance, metadata, age, dataset, and payload gates stay active."),
|
|
@@ -563,8 +588,8 @@ def render_provider_cards(relay_status: dict | None = None, adult_mode: bool = F
|
|
| 563 |
relay_status = relay_status or {}
|
| 564 |
decisions = relay_status.get("decisions", [])
|
| 565 |
optional_statuses = {
|
| 566 |
-
"openbmb": "configured" if
|
| 567 |
-
"nvidia": "configured" if
|
| 568 |
"fal": "configured" if _env_configured("FAL_KEY") else "blocked",
|
| 569 |
"netlify": "configured" if _env_configured("NETLIFY_AUTH_TOKEN", "NETLIFY_SITE_ID", "OPENAI_BASE_URL") else "blocked",
|
| 570 |
"cloudflare": "configured" if _env_configured("CLOUDFLARE_API_TOKEN", "CF_ACCOUNT_ID") else "blocked",
|
|
@@ -664,10 +689,10 @@ def render_inspector(
|
|
| 664 |
("NVIDIA Nemotron", "nvidia/NVIDIA-Nemotron-Parse-v1.2", nemotron),
|
| 665 |
]
|
| 666 |
)
|
| 667 |
-
findings = scan.get("findings") or []
|
| 668 |
-
actions = scan.get("purification_actions") or ["metadata strip ready", "IEND truncation ready", "LSB review ready"]
|
| 669 |
-
finding_rows = "".join(f"<li>{escape(
|
| 670 |
-
action_rows = "".join(f"<li>{escape(
|
| 671 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 672 |
return f"""
|
| 673 |
<aside class="nw-panel nw-inspector">
|
|
|
|
| 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 |
+
|
| 55 |
+
_SCAN_REDACTION_TERMS = (
|
| 56 |
+
"payload",
|
| 57 |
+
"payload_excerpt",
|
| 58 |
+
"hidden content",
|
| 59 |
+
"recovered",
|
| 60 |
+
"raw byte",
|
| 61 |
+
"base64",
|
| 62 |
+
"hex dump",
|
| 63 |
+
)
|
| 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):
|
| 70 |
+
return "Redacted scan detail; review the gated ST3GG evidence packet."
|
| 71 |
+
if len(text) > 180:
|
| 72 |
+
return text[:177] + "..."
|
| 73 |
+
return text
|
| 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"
|
|
|
|
| 80 |
configured = []
|
| 81 |
if _env_configured("HF_TOKEN", "HUGGING_FACE_HUB_TOKEN"):
|
| 82 |
configured.append("HF")
|
| 83 |
+
if _provider_configured("MINICPM_BASE_URL", "MINICPM_API_KEY", "OPENBMB_API_KEY"):
|
| 84 |
configured.append("MiniCPM")
|
| 85 |
+
if _provider_configured("NEMOTRON_BASE_URL", "NEMOTRON_API_KEY", "NVIDIA_API_KEY"):
|
| 86 |
configured.append("Nemotron")
|
| 87 |
if _env_configured("FAL_KEY", "NETLIFY_AUTH_TOKEN", "OPENAI_API_KEY", "MODAL_TOKEN_ID"):
|
| 88 |
configured.append("optional")
|
|
|
|
| 131 |
status = str(scan.get("status", "idle")).upper()
|
| 132 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 133 |
checkpoint = str(operator_state.get("checkpoint", "pending")).replace("_", " ").title()
|
| 134 |
+
findings = [_redact_scan_text(item) for item in (scan.get("findings") or ["No upload selected. Always-on scanner ready."])]
|
| 135 |
+
actions = [_redact_scan_text(item) for item in (scan.get("purification_actions") or [
|
| 136 |
"strip metadata before export",
|
| 137 |
"truncate PNG after IEND when needed",
|
| 138 |
"run LSB statistical review",
|
| 139 |
+
])]
|
| 140 |
tone = _scan_status_tone(str(scan.get("status", "idle")))
|
| 141 |
return f"""
|
| 142 |
<section class="nw-trust-strip">
|
|
|
|
| 485 |
("ST3GG state", f"{scan_status} / export {export_gate}"),
|
| 486 |
(
|
| 487 |
"Findings",
|
| 488 |
+
"; ".join(_redact_scan_text(item) for item in (scan.get("findings") or [])[:2])
|
| 489 |
or ("No findings." if scan_status != "IDLE" else "No upload selected."),
|
| 490 |
),
|
| 491 |
("Public export", "Consent, provenance, metadata, age, dataset, and payload gates stay active."),
|
|
|
|
| 588 |
relay_status = relay_status or {}
|
| 589 |
decisions = relay_status.get("decisions", [])
|
| 590 |
optional_statuses = {
|
| 591 |
+
"openbmb": "configured" if _provider_configured("MINICPM_BASE_URL", "MINICPM_API_KEY", "OPENBMB_API_KEY") else "missing secret",
|
| 592 |
+
"nvidia": "configured" if _provider_configured("NEMOTRON_BASE_URL", "NEMOTRON_API_KEY", "NVIDIA_API_KEY") else "missing secret",
|
| 593 |
"fal": "configured" if _env_configured("FAL_KEY") else "blocked",
|
| 594 |
"netlify": "configured" if _env_configured("NETLIFY_AUTH_TOKEN", "NETLIFY_SITE_ID", "OPENAI_BASE_URL") else "blocked",
|
| 595 |
"cloudflare": "configured" if _env_configured("CLOUDFLARE_API_TOKEN", "CF_ACCOUNT_ID") else "blocked",
|
|
|
|
| 689 |
("NVIDIA Nemotron", "nvidia/NVIDIA-Nemotron-Parse-v1.2", nemotron),
|
| 690 |
]
|
| 691 |
)
|
| 692 |
+
findings = [_redact_scan_text(item) for item in (scan.get("findings") or [])]
|
| 693 |
+
actions = [_redact_scan_text(item) for item in (scan.get("purification_actions") or ["metadata strip ready", "IEND truncation ready", "LSB review ready"])]
|
| 694 |
+
finding_rows = "".join(f"<li>{escape(item)}</li>" for item in findings[:4]) or "<li>No upload selected. Scanner ready.</li>"
|
| 695 |
+
action_rows = "".join(f"<li>{escape(item)}</li>" for item in actions[:4])
|
| 696 |
export_gate = str(scan.get("export_gate", "pending")).upper()
|
| 697 |
return f"""
|
| 698 |
<aside class="nw-panel nw-inspector">
|
tests/test_app_callbacks.py
CHANGED
|
@@ -1,4 +1,86 @@
|
|
| 1 |
import app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
|
| 4 |
def test_run_weave_returns_stateful_dashboard_packet() -> None:
|
|
@@ -18,6 +100,8 @@ def test_run_weave_returns_stateful_dashboard_packet() -> None:
|
|
| 18 |
|
| 19 |
|
| 20 |
def test_operator_actions_transition_checkpoint_export_and_stop() -> None:
|
|
|
|
|
|
|
| 21 |
result = app.run_weave(
|
| 22 |
"gothic patent leather platform boots, crimson hardware",
|
| 23 |
"Strict",
|
|
@@ -27,7 +111,10 @@ def test_operator_actions_transition_checkpoint_export_and_stop() -> None:
|
|
| 27 |
"Forge",
|
| 28 |
)
|
| 29 |
run = result[13]
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
| 31 |
clean_scan = {"status": "pass", "export_gate": "clear", "findings": [], "purification_actions": []}
|
| 32 |
|
| 33 |
approved = app.approve_checkpoint(run, False, clean_scan, "Forge", operator_state)
|
|
@@ -59,6 +146,66 @@ def test_export_blocks_without_checkpoint() -> None:
|
|
| 59 |
assert "checkpoint" in blocked[13]["message"].lower()
|
| 60 |
|
| 61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 62 |
def test_checkpoint_blocks_without_generated_artifact() -> None:
|
| 63 |
result = app.run_weave(
|
| 64 |
"gothic patent leather platform boots, crimson hardware",
|
|
|
|
| 1 |
import app
|
| 2 |
+
from nexus_visual_weaver.planner import build_command_center_run
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
# --- _checkpoint_seed tests ---
|
| 6 |
+
|
| 7 |
+
def test_checkpoint_seed_parses_valid_hex_suffix() -> None:
|
| 8 |
+
# "nw-" prefix + "abcdef12" -> int("abcdef12", 16) % 1_000_000
|
| 9 |
+
checkpoint_id = "nw-abcdef12"
|
| 10 |
+
result = app._checkpoint_seed(checkpoint_id)
|
| 11 |
+
assert result == int("abcdef12", 16) % 1_000_000
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_checkpoint_seed_handles_empty_suffix() -> None:
|
| 15 |
+
assert app._checkpoint_seed("") == 0
|
| 16 |
+
assert app._checkpoint_seed("nw-") == 0
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def test_checkpoint_seed_handles_non_hex_suffix() -> None:
|
| 20 |
+
# No hex chars -> return 0
|
| 21 |
+
result = app._checkpoint_seed("nw-zzzzzzzz")
|
| 22 |
+
assert result == 0
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_checkpoint_seed_ignores_non_hex_chars_in_last_8() -> None:
|
| 26 |
+
# Strips non-hex: "abc-xyz" -> only "abcef" are hex in the last 8 chars ("-xyz" strips to "abc")
|
| 27 |
+
# "0000000g" -> only "0000000" are valid hex
|
| 28 |
+
result = app._checkpoint_seed("0000000g")
|
| 29 |
+
assert result == int("0000000", 16) % 1_000_000
|
| 30 |
+
assert result == 0
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
def test_checkpoint_seed_returns_value_within_range() -> None:
|
| 34 |
+
checkpoint_id = "nw-deadbeef"
|
| 35 |
+
result = app._checkpoint_seed(checkpoint_id)
|
| 36 |
+
assert 0 <= result < 1_000_000
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_checkpoint_seed_uses_only_last_8_chars() -> None:
|
| 40 |
+
# Long checkpoint id: only last 8 chars considered
|
| 41 |
+
# "nw-aabbccddee11223344" -> last 8 is "11223344"
|
| 42 |
+
result = app._checkpoint_seed("nw-aabbccddee11223344")
|
| 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:
|
| 49 |
+
run = build_command_center_run("gothic patent leather platform boots, crimson hardware")
|
| 50 |
+
summary = app._wardrobe_summary(run)
|
| 51 |
+
|
| 52 |
+
# Summary is a semicolon-delimited string of slot info
|
| 53 |
+
assert isinstance(summary, str)
|
| 54 |
+
assert len(summary) > 0
|
| 55 |
+
# Should contain material= and palette= from slot info
|
| 56 |
+
assert "material=" in summary
|
| 57 |
+
assert "palette=" in summary
|
| 58 |
+
assert "locked=" in summary
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_wardrobe_summary_handles_run_with_no_outfit() -> None:
|
| 62 |
+
class FakeRun:
|
| 63 |
+
outfit = None
|
| 64 |
+
|
| 65 |
+
summary = app._wardrobe_summary(FakeRun())
|
| 66 |
+
assert summary == ""
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def test_wardrobe_summary_handles_none_run() -> None:
|
| 70 |
+
# Pass something with no outfit attribute
|
| 71 |
+
class Empty:
|
| 72 |
+
pass
|
| 73 |
+
|
| 74 |
+
summary = app._wardrobe_summary(Empty())
|
| 75 |
+
assert summary == ""
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_wardrobe_summary_includes_slot_names() -> None:
|
| 79 |
+
run = build_command_center_run("dark couture archivist, patent leather, platform boots")
|
| 80 |
+
summary = app._wardrobe_summary(run)
|
| 81 |
+
|
| 82 |
+
# Slot names like footwear, outerwear, upper_body should appear
|
| 83 |
+
assert any(slot_name in summary for slot_name in ["footwear", "outerwear", "upper_body", "jewelry"])
|
| 84 |
|
| 85 |
|
| 86 |
def test_run_weave_returns_stateful_dashboard_packet() -> None:
|
|
|
|
| 100 |
|
| 101 |
|
| 102 |
def test_operator_actions_transition_checkpoint_export_and_stop() -> None:
|
| 103 |
+
from pathlib import Path
|
| 104 |
+
|
| 105 |
result = app.run_weave(
|
| 106 |
"gothic patent leather platform boots, crimson hardware",
|
| 107 |
"Strict",
|
|
|
|
| 111 |
"Forge",
|
| 112 |
)
|
| 113 |
run = result[13]
|
| 114 |
+
artifact_path = Path("outputs/test-generated-artifact.png")
|
| 115 |
+
artifact_path.parent.mkdir(parents=True, exist_ok=True)
|
| 116 |
+
artifact_path.write_bytes(b"\x89PNG\r\n\x1a\n" + b"\x00" * 100)
|
| 117 |
+
operator_state = {**result[15], "generation": {**result[15]["generation"], "output_path": str(artifact_path)}}
|
| 118 |
clean_scan = {"status": "pass", "export_gate": "clear", "findings": [], "purification_actions": []}
|
| 119 |
|
| 120 |
approved = app.approve_checkpoint(run, False, clean_scan, "Forge", operator_state)
|
|
|
|
| 146 |
assert "checkpoint" in blocked[13]["message"].lower()
|
| 147 |
|
| 148 |
|
| 149 |
+
def test_reference_scan_cannot_clear_blocked_generated_artifact() -> None:
|
| 150 |
+
base = app.ROOT / "outputs" / "test-app-callbacks"
|
| 151 |
+
base.mkdir(parents=True, exist_ok=True)
|
| 152 |
+
blocked_artifact = base / "blocked-generated.png"
|
| 153 |
+
blocked_artifact.write_bytes(
|
| 154 |
+
b"\x89PNG\r\n\x1a\n"
|
| 155 |
+
b"\x00\x00\x00\rIEND\xaeB`\x82"
|
| 156 |
+
b"NEXUS_TRAILING_PAYLOAD"
|
| 157 |
+
)
|
| 158 |
+
clean_reference = base / "clean-reference.png"
|
| 159 |
+
clean_reference.write_bytes(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIEND\xaeB`\x82")
|
| 160 |
+
run = build_command_center_run("gothic patent leather platform boots")
|
| 161 |
+
state = {
|
| 162 |
+
"provider_state": "checkpointed",
|
| 163 |
+
"checkpoint": "pending_review",
|
| 164 |
+
"export": "blocked",
|
| 165 |
+
"generation": {"status": "success", "output_path": str(blocked_artifact)},
|
| 166 |
+
"generated_scan": app.scan_file(str(blocked_artifact)),
|
| 167 |
+
}
|
| 168 |
+
|
| 169 |
+
scanned = app.scan_reference(run, False, str(clean_reference), "Forge", state)
|
| 170 |
+
assert scanned[13]["reference_scan"]["export_gate"] == "clear"
|
| 171 |
+
assert scanned[13]["export"] == "blocked"
|
| 172 |
+
assert scanned[15]["export_gate"] == "blocked"
|
| 173 |
+
|
| 174 |
+
approved = app.approve_checkpoint(run, False, scanned[15], "Forge", scanned[13])
|
| 175 |
+
assert approved[13]["provider_state"] == "checkpointed"
|
| 176 |
+
assert approved[13]["export"] == "blocked"
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_blocked_reference_scan_does_not_block_clear_generated_artifact() -> None:
|
| 180 |
+
base = app.ROOT / "outputs" / "test-app-callbacks"
|
| 181 |
+
base.mkdir(parents=True, exist_ok=True)
|
| 182 |
+
generated = base / "clear-generated.png"
|
| 183 |
+
generated.write_bytes(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIEND\xaeB`\x82")
|
| 184 |
+
blocked_reference = base / "blocked-reference.png"
|
| 185 |
+
blocked_reference.write_bytes(
|
| 186 |
+
b"\x89PNG\r\n\x1a\n"
|
| 187 |
+
b"\x00\x00\x00\rIEND\xaeB`\x82"
|
| 188 |
+
b"NEXUS_TRAILING_PAYLOAD"
|
| 189 |
+
)
|
| 190 |
+
run = build_command_center_run("gothic patent leather platform boots")
|
| 191 |
+
state = {
|
| 192 |
+
"provider_state": "checkpointed",
|
| 193 |
+
"checkpoint": "pending_review",
|
| 194 |
+
"export": "clear",
|
| 195 |
+
"generation": {"status": "success", "output_path": str(generated)},
|
| 196 |
+
"generated_scan": app.scan_file(str(generated)),
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
scanned = app.scan_reference(run, False, str(blocked_reference), "Forge", state)
|
| 200 |
+
assert scanned[13]["reference_scan"]["export_gate"] == "blocked"
|
| 201 |
+
assert scanned[13]["export"] == "clear"
|
| 202 |
+
assert scanned[15]["export_gate"] == "clear"
|
| 203 |
+
|
| 204 |
+
approved = app.approve_checkpoint(run, False, scanned[15], "Forge", scanned[13])
|
| 205 |
+
assert approved[13]["provider_state"] == "export_ready"
|
| 206 |
+
assert approved[13]["export"] == "clear"
|
| 207 |
+
|
| 208 |
+
|
| 209 |
def test_checkpoint_blocks_without_generated_artifact() -> None:
|
| 210 |
result = app.run_weave(
|
| 211 |
"gothic patent leather platform boots, crimson hardware",
|
tests/test_command_center.py
CHANGED
|
@@ -1,10 +1,26 @@
|
|
| 1 |
from pathlib import Path
|
| 2 |
|
| 3 |
-
from nexus_visual_weaver.catalog import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
from nexus_visual_weaver.grounding import inspect_outfit
|
| 5 |
from nexus_visual_weaver.model_relay import WeaverModelRelay
|
| 6 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 7 |
-
from nexus_visual_weaver.render import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
from nexus_visual_weaver.security import scan_file
|
| 9 |
from nexus_visual_weaver.taste import refine_prompt, score_prompt
|
| 10 |
from nexus_visual_weaver.wardrobe import build_outfit_graph
|
|
@@ -218,3 +234,283 @@ def test_catalog_summary_reflects_adult_scope() -> None:
|
|
| 218 |
assert default_summary["adult_catalog"] == "hidden"
|
| 219 |
assert adult_summary["adult_catalog"] == "enabled"
|
| 220 |
assert adult_summary["models_visible"] > default_summary["models_visible"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from pathlib import Path
|
| 2 |
|
| 3 |
+
from nexus_visual_weaver.catalog import (
|
| 4 |
+
DEFAULT_ACTIVE_STACK,
|
| 5 |
+
PRIVATE_RESEARCH_STACK,
|
| 6 |
+
active_stack,
|
| 7 |
+
catalog_summary,
|
| 8 |
+
filter_catalog,
|
| 9 |
+
parameter_budget,
|
| 10 |
+
)
|
| 11 |
from nexus_visual_weaver.grounding import inspect_outfit
|
| 12 |
from nexus_visual_weaver.model_relay import WeaverModelRelay
|
| 13 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 14 |
+
from nexus_visual_weaver.render import (
|
| 15 |
+
render_command_header,
|
| 16 |
+
render_dashboard_regions,
|
| 17 |
+
render_inspector,
|
| 18 |
+
render_operations_panel,
|
| 19 |
+
render_provider_cards,
|
| 20 |
+
render_topbar,
|
| 21 |
+
render_trust_strip,
|
| 22 |
+
)
|
| 23 |
+
from nexus_visual_weaver.schema import ModelCandidate
|
| 24 |
from nexus_visual_weaver.security import scan_file
|
| 25 |
from nexus_visual_weaver.taste import refine_prompt, score_prompt
|
| 26 |
from nexus_visual_weaver.wardrobe import build_outfit_graph
|
|
|
|
| 234 |
assert default_summary["adult_catalog"] == "hidden"
|
| 235 |
assert adult_summary["adult_catalog"] == "enabled"
|
| 236 |
assert adult_summary["models_visible"] > default_summary["models_visible"]
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
# --- render_trust_strip tests ---
|
| 240 |
+
|
| 241 |
+
def test_render_trust_strip_defaults_to_idle_state() -> None:
|
| 242 |
+
html = render_trust_strip()
|
| 243 |
+
|
| 244 |
+
assert "TRUST MODEL" in html
|
| 245 |
+
assert "Generation is not export." in html
|
| 246 |
+
assert "ST3GG IDLE" in html
|
| 247 |
+
assert "EXPORT PENDING" in html
|
| 248 |
+
assert "Clean PNG -> pass." in html
|
| 249 |
+
assert "FIXTURE EVIDENCE" in html
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
def test_render_trust_strip_pass_scan_shows_clear_export() -> None:
|
| 253 |
+
scan = {"status": "pass", "export_gate": "clear", "findings": ["all checks passed"], "purification_actions": ["none needed"]}
|
| 254 |
+
html = render_trust_strip(scan=scan)
|
| 255 |
+
|
| 256 |
+
assert "ST3GG PASS" in html
|
| 257 |
+
assert "EXPORT CLEAR" in html
|
| 258 |
+
assert "all checks passed" in html
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def test_render_trust_strip_review_scan_shows_blocked_export() -> None:
|
| 262 |
+
scan = {"status": "review", "export_gate": "blocked", "findings": ["trailing data after IEND"], "purification_actions": ["truncate PNG"]}
|
| 263 |
+
html = render_trust_strip(scan=scan)
|
| 264 |
+
|
| 265 |
+
assert "ST3GG REVIEW" in html
|
| 266 |
+
assert "EXPORT BLOCKED" in html
|
| 267 |
+
assert "trailing data after IEND" in html
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
def test_render_trust_strip_redacts_payload_details() -> None:
|
| 271 |
+
scan = {
|
| 272 |
+
"status": "review",
|
| 273 |
+
"export_gate": "blocked",
|
| 274 |
+
"findings": ["payload excerpt: deadbeef hidden content recovered"],
|
| 275 |
+
"purification_actions": ["raw byte hex dump should stay quarantined"],
|
| 276 |
+
}
|
| 277 |
+
html = render_trust_strip(scan=scan)
|
| 278 |
+
|
| 279 |
+
assert "Redacted scan detail" in html
|
| 280 |
+
assert "deadbeef" not in html
|
| 281 |
+
assert "hex dump" not in html
|
| 282 |
+
|
| 283 |
+
|
| 284 |
+
def test_render_trust_strip_approved_checkpoint_shows_pass() -> None:
|
| 285 |
+
operator_state = {"checkpoint": "approved", "provider_state": "export_ready"}
|
| 286 |
+
html = render_trust_strip(operator_state=operator_state)
|
| 287 |
+
|
| 288 |
+
assert "CHECKPOINT APPROVED" in html
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
def test_render_trust_strip_pending_review_checkpoint_label() -> None:
|
| 292 |
+
operator_state = {"checkpoint": "pending_review"}
|
| 293 |
+
html = render_trust_strip(operator_state=operator_state)
|
| 294 |
+
|
| 295 |
+
assert "CHECKPOINT PENDING REVIEW" in html
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
# --- render_topbar tests ---
|
| 299 |
+
|
| 300 |
+
def test_render_topbar_includes_trust_strip() -> None:
|
| 301 |
+
html = render_topbar()
|
| 302 |
+
|
| 303 |
+
assert "TRUST MODEL" in html
|
| 304 |
+
assert "nw-trust-strip" in html
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def test_render_topbar_with_scan_passes_to_trust_strip() -> None:
|
| 308 |
+
scan = {"status": "pass", "export_gate": "clear", "findings": ["no issues"], "purification_actions": []}
|
| 309 |
+
html = render_topbar(scan=scan)
|
| 310 |
+
|
| 311 |
+
assert "ST3GG PASS" in html
|
| 312 |
+
assert "EXPORT CLEAR" in html
|
| 313 |
+
|
| 314 |
+
|
| 315 |
+
def test_render_topbar_with_operator_state_shows_checkpoint() -> None:
|
| 316 |
+
operator_state = {"checkpoint": "approved"}
|
| 317 |
+
html = render_topbar(operator_state=operator_state)
|
| 318 |
+
|
| 319 |
+
assert "CHECKPOINT APPROVED" in html
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def test_render_topbar_default_scan_shows_fixture_evidence() -> None:
|
| 323 |
+
html = render_topbar()
|
| 324 |
+
|
| 325 |
+
assert "Clean PNG -> pass." in html
|
| 326 |
+
assert "PNG trailing bytes -> blocked." in html
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
# --- render_inspector sponsor evidence tests ---
|
| 330 |
+
|
| 331 |
+
def test_render_inspector_shows_sponsor_evidence_section() -> None:
|
| 332 |
+
html = render_inspector()
|
| 333 |
+
|
| 334 |
+
assert "Sponsor Evidence" in html
|
| 335 |
+
assert "OpenBMB MiniCPM" in html
|
| 336 |
+
assert "NVIDIA Nemotron" in html
|
| 337 |
+
|
| 338 |
+
|
| 339 |
+
def test_render_inspector_with_missing_secret_shows_pending() -> None:
|
| 340 |
+
operator_state = {
|
| 341 |
+
"minicpm_judge": {"status": "missing_secret", "repo_id": "openbmb/MiniCPM-V-4.6"},
|
| 342 |
+
"nemotron_evidence": {"status": "missing_secret", "repo_id": "nvidia/NVIDIA-Nemotron-Parse-v1.2"},
|
| 343 |
+
}
|
| 344 |
+
html = render_inspector(operator_state=operator_state)
|
| 345 |
+
|
| 346 |
+
assert "MISSING_SECRET" in html
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def test_render_inspector_with_success_judge_shows_success_status() -> None:
|
| 350 |
+
operator_state = {
|
| 351 |
+
"minicpm_judge": {"status": "success", "repo_id": "openbmb/MiniCPM-V-4.6"},
|
| 352 |
+
"nemotron_evidence": {"status": "success", "repo_id": "nvidia/NVIDIA-Nemotron-Parse-v1.2"},
|
| 353 |
+
}
|
| 354 |
+
html = render_inspector(operator_state=operator_state)
|
| 355 |
+
|
| 356 |
+
assert html.count("SUCCESS") >= 2
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def test_render_inspector_shows_default_stack_label_without_run() -> None:
|
| 360 |
+
html = render_inspector()
|
| 361 |
+
|
| 362 |
+
assert "FLUX.2 4B / MiniCPM / LocateAnything" in html
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
# --- render_provider_cards sponsor lane tests ---
|
| 366 |
+
|
| 367 |
+
def test_render_provider_cards_shows_openbmb_and_nvidia_entries() -> None:
|
| 368 |
+
html = render_provider_cards()
|
| 369 |
+
|
| 370 |
+
assert "Openbmb" in html or "openbmb" in html.lower()
|
| 371 |
+
assert "Nvidia" in html or "nvidia" in html.lower()
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def test_render_provider_cards_shows_sponsor_lane_badge_for_openbmb(monkeypatch) -> None:
|
| 375 |
+
monkeypatch.setenv("MINICPM_API_KEY", "test-key")
|
| 376 |
+
monkeypatch.setenv("MINICPM_BASE_URL", "https://minicpm.example.test")
|
| 377 |
+
html = render_provider_cards()
|
| 378 |
+
|
| 379 |
+
assert "SPONSOR LANE" in html
|
| 380 |
+
|
| 381 |
+
|
| 382 |
+
def test_render_provider_cards_shows_missing_secret_for_unconfigured_sponsor(monkeypatch) -> None:
|
| 383 |
+
monkeypatch.delenv("MINICPM_API_KEY", raising=False)
|
| 384 |
+
monkeypatch.delenv("OPENBMB_API_KEY", raising=False)
|
| 385 |
+
monkeypatch.delenv("NEMOTRON_API_KEY", raising=False)
|
| 386 |
+
monkeypatch.delenv("NVIDIA_API_KEY", raising=False)
|
| 387 |
+
html = render_provider_cards()
|
| 388 |
+
|
| 389 |
+
assert "MISSING SECRET" in html
|
| 390 |
+
|
| 391 |
+
|
| 392 |
+
def test_render_provider_cards_configured_openbmb_shows_configured(monkeypatch) -> None:
|
| 393 |
+
monkeypatch.setenv("MINICPM_API_KEY", "test-key")
|
| 394 |
+
monkeypatch.delenv("MINICPM_BASE_URL", raising=False)
|
| 395 |
+
monkeypatch.delenv("NEMOTRON_API_KEY", raising=False)
|
| 396 |
+
monkeypatch.delenv("NVIDIA_API_KEY", raising=False)
|
| 397 |
+
monkeypatch.delenv("NEMOTRON_BASE_URL", raising=False)
|
| 398 |
+
monkeypatch.delenv("FAL_KEY", raising=False)
|
| 399 |
+
monkeypatch.delenv("NETLIFY_AUTH_TOKEN", raising=False)
|
| 400 |
+
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
|
| 401 |
+
monkeypatch.delenv("CLOUDFLARE_API_TOKEN", raising=False)
|
| 402 |
+
html = render_provider_cards()
|
| 403 |
+
|
| 404 |
+
assert "CONFIGURED" not in html
|
| 405 |
+
assert "MISSING SECRET" in html
|
| 406 |
+
|
| 407 |
+
|
| 408 |
+
def test_render_provider_cards_configured_openbmb_requires_key_and_base_url(monkeypatch) -> None:
|
| 409 |
+
monkeypatch.setenv("MINICPM_API_KEY", "test-key")
|
| 410 |
+
monkeypatch.setenv("MINICPM_BASE_URL", "https://minicpm.example.test")
|
| 411 |
+
html = render_provider_cards()
|
| 412 |
+
|
| 413 |
+
assert "CONFIGURED" in html
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
def test_render_operations_and_inspector_redact_payload_details() -> None:
|
| 417 |
+
scan = {
|
| 418 |
+
"status": "review",
|
| 419 |
+
"export_gate": "blocked",
|
| 420 |
+
"findings": ["payload excerpt: recovered hidden content"],
|
| 421 |
+
"purification_actions": ["base64 raw bytes quarantined"],
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
operations = render_operations_panel(active_section="Security", scan=scan)
|
| 425 |
+
inspector = render_inspector(scan=scan)
|
| 426 |
+
|
| 427 |
+
assert "Redacted scan detail" in operations
|
| 428 |
+
assert "Redacted scan detail" in inspector
|
| 429 |
+
assert "recovered hidden content" not in operations
|
| 430 |
+
assert "base64 raw bytes" not in inspector
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
# --- catalog public_demo field tests ---
|
| 434 |
+
|
| 435 |
+
def test_filter_catalog_excludes_flux_9b_in_public_mode() -> None:
|
| 436 |
+
models, _ = filter_catalog(False)
|
| 437 |
+
repo_ids = {model.repo_id for model in models}
|
| 438 |
+
|
| 439 |
+
assert "black-forest-labs/FLUX.2-klein-9B" not in repo_ids
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def test_filter_catalog_includes_flux_9b_in_adult_mode() -> None:
|
| 443 |
+
models, _ = filter_catalog(True)
|
| 444 |
+
repo_ids = {model.repo_id for model in models}
|
| 445 |
+
|
| 446 |
+
assert "black-forest-labs/FLUX.2-klein-9B" in repo_ids
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def test_filter_catalog_excludes_offellia_in_public_mode() -> None:
|
| 450 |
+
models, _ = filter_catalog(False)
|
| 451 |
+
repo_ids = {model.repo_id for model in models}
|
| 452 |
+
|
| 453 |
+
assert not any("OFFELLIA" in repo_id for repo_id in repo_ids)
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
def test_active_stack_uses_private_research_stack_in_adult_mode() -> None:
|
| 457 |
+
stack = active_stack(True)
|
| 458 |
+
repo_ids = {model.repo_id for model in stack}
|
| 459 |
+
|
| 460 |
+
assert "black-forest-labs/FLUX.2-klein-9B" in repo_ids
|
| 461 |
+
assert "black-forest-labs/FLUX.2-klein-4B" not in repo_ids
|
| 462 |
+
|
| 463 |
+
|
| 464 |
+
def test_private_research_stack_constant_contains_9b_and_offellia() -> None:
|
| 465 |
+
assert "black-forest-labs/FLUX.2-klein-9B" in PRIVATE_RESEARCH_STACK
|
| 466 |
+
assert "Brunobkr/OFFELLIA_Q4_0_gemma-4-12B-it.gguf" in PRIVATE_RESEARCH_STACK
|
| 467 |
+
|
| 468 |
+
|
| 469 |
+
def test_default_active_stack_constant_uses_4b_and_sponsor_models() -> None:
|
| 470 |
+
assert "black-forest-labs/FLUX.2-klein-4B" in DEFAULT_ACTIVE_STACK
|
| 471 |
+
assert "openbmb/MiniCPM-V-4.6" in DEFAULT_ACTIVE_STACK
|
| 472 |
+
assert "nvidia/NVIDIA-Nemotron-Parse-v1.2" in DEFAULT_ACTIVE_STACK
|
| 473 |
+
assert "black-forest-labs/FLUX.2-klein-9B" not in DEFAULT_ACTIVE_STACK
|
| 474 |
+
|
| 475 |
+
|
| 476 |
+
# --- schema ModelCandidate public_demo tests ---
|
| 477 |
+
|
| 478 |
+
def test_model_candidate_public_demo_defaults_to_true() -> None:
|
| 479 |
+
candidate = ModelCandidate(
|
| 480 |
+
repo_id="test/model",
|
| 481 |
+
role="test_role",
|
| 482 |
+
task="text-to-image",
|
| 483 |
+
params_b=1.0,
|
| 484 |
+
runtime="local",
|
| 485 |
+
license="apache-2.0",
|
| 486 |
+
)
|
| 487 |
+
assert candidate.public_demo is True
|
| 488 |
+
|
| 489 |
+
|
| 490 |
+
def test_model_candidate_public_demo_can_be_set_false() -> None:
|
| 491 |
+
candidate = ModelCandidate(
|
| 492 |
+
repo_id="test/private-model",
|
| 493 |
+
role="private_role",
|
| 494 |
+
task="text-to-image",
|
| 495 |
+
params_b=9.0,
|
| 496 |
+
runtime="gated provider",
|
| 497 |
+
license="other",
|
| 498 |
+
public_demo=False,
|
| 499 |
+
)
|
| 500 |
+
assert candidate.public_demo is False
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
def test_public_demo_false_models_are_excluded_from_public_filter() -> None:
|
| 504 |
+
private = ModelCandidate(
|
| 505 |
+
repo_id="test/hidden-model",
|
| 506 |
+
role="private",
|
| 507 |
+
task="text-to-image",
|
| 508 |
+
params_b=2.0,
|
| 509 |
+
runtime="local",
|
| 510 |
+
license="other",
|
| 511 |
+
public_demo=False,
|
| 512 |
+
)
|
| 513 |
+
# public_demo=False should mean filter_catalog(False) excludes it
|
| 514 |
+
# The catalog-level test: verify FLUX 9B (public_demo=False) is absent
|
| 515 |
+
models_public, _ = filter_catalog(False)
|
| 516 |
+
assert all(m.public_demo for m in models_public)
|
tests/test_exporter.py
CHANGED
|
@@ -1,15 +1,11 @@
|
|
| 1 |
import json
|
| 2 |
from pathlib import Path
|
| 3 |
|
| 4 |
-
from nexus_visual_weaver.exporter import write_export_packet
|
| 5 |
from nexus_visual_weaver.planner import build_command_center_run
|
| 6 |
|
| 7 |
|
| 8 |
-
def
|
| 9 |
-
export_dir = Path("outputs/test-exports")
|
| 10 |
-
monkeypatch.setenv("NEXUS_EXPORT_DIR", str(export_dir))
|
| 11 |
-
run = build_command_center_run("gothic couture archivist, platform boots")
|
| 12 |
-
scan = {"status": "pass", "export_gate": "clear", "findings": [], "purification_actions": []}
|
| 13 |
state = {
|
| 14 |
"provider_state": "export_ready",
|
| 15 |
"checkpoint": "approved",
|
|
@@ -18,6 +14,16 @@ def test_write_export_packet_records_evidence_without_secrets(monkeypatch) -> No
|
|
| 18 |
"minicpm_judge": {"status": "success", "repo_id": "openbmb/MiniCPM-V-4.6"},
|
| 19 |
"nemotron_evidence": {"status": "missing_secret", "repo_id": "nvidia/NVIDIA-Nemotron-Parse-v1.2"},
|
| 20 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 23 |
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
|
@@ -27,3 +33,154 @@ def test_write_export_packet_records_evidence_without_secrets(monkeypatch) -> No
|
|
| 27 |
assert payload["hackathon_claims"]["nvidia_nemotron_lane"] is False
|
| 28 |
assert payload["parameter_budget"]["status"] == "pass"
|
| 29 |
assert "token" not in json.dumps(payload).lower()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import json
|
| 2 |
from pathlib import Path
|
| 3 |
|
| 4 |
+
from nexus_visual_weaver.exporter import export_root, write_export_packet
|
| 5 |
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",
|
|
|
|
| 14 |
"minicpm_judge": {"status": "success", "repo_id": "openbmb/MiniCPM-V-4.6"},
|
| 15 |
"nemotron_evidence": {"status": "missing_secret", "repo_id": "nvidia/NVIDIA-Nemotron-Parse-v1.2"},
|
| 16 |
}
|
| 17 |
+
state.update(overrides)
|
| 18 |
+
return state
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_write_export_packet_records_evidence_without_secrets(monkeypatch) -> None:
|
| 22 |
+
export_dir = Path("outputs/test-exports")
|
| 23 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", str(export_dir))
|
| 24 |
+
run = build_command_center_run("gothic couture archivist, platform boots")
|
| 25 |
+
scan = {"status": "pass", "export_gate": "clear", "findings": [], "purification_actions": []}
|
| 26 |
+
state = _make_base_state()
|
| 27 |
|
| 28 |
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 29 |
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
|
|
|
| 33 |
assert payload["hackathon_claims"]["nvidia_nemotron_lane"] is False
|
| 34 |
assert payload["parameter_budget"]["status"] == "pass"
|
| 35 |
assert "token" not in json.dumps(payload).lower()
|
| 36 |
+
assert payload["artifact"] == "artifact.png"
|
| 37 |
+
assert payload["generation"]["output_path"] == "artifact.png"
|
| 38 |
+
assert "/data/" not in json.dumps(payload)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_export_packet_has_correct_schema_version(monkeypatch) -> None:
|
| 42 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 43 |
+
run = build_command_center_run("dark couture brief")
|
| 44 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 45 |
+
state = _make_base_state()
|
| 46 |
+
|
| 47 |
+
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 48 |
+
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
| 49 |
+
|
| 50 |
+
assert payload["schema"] == "nexus_visual_weaver.export_packet.v1"
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def test_export_packet_stores_adult_mode_flag(monkeypatch) -> None:
|
| 54 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 55 |
+
run_public = build_command_center_run("dark couture brief", adult_mode=False)
|
| 56 |
+
run_private = build_command_center_run("dark couture brief", adult_mode=True)
|
| 57 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 58 |
+
|
| 59 |
+
result_public = write_export_packet(run=run_public, scan=scan, operator_state=_make_base_state(), adult_mode=True)
|
| 60 |
+
result_private = write_export_packet(run=run_private, scan=scan, operator_state=_make_base_state(), adult_mode=False)
|
| 61 |
+
|
| 62 |
+
public_payload = json.loads(Path(result_public["path"]).read_text(encoding="utf-8"))
|
| 63 |
+
private_payload = json.loads(Path(result_private["path"]).read_text(encoding="utf-8"))
|
| 64 |
+
|
| 65 |
+
assert public_payload["adult_mode"] is False
|
| 66 |
+
assert private_payload["adult_mode"] is True
|
| 67 |
+
assert public_payload["model_stack"][0]["repo_id"] == run_public.model_stack[0].repo_id
|
| 68 |
+
assert private_payload["model_stack"][0]["repo_id"] == run_private.model_stack[0].repo_id
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def test_export_packet_removes_hf_token_from_generation(monkeypatch) -> None:
|
| 72 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 73 |
+
run = build_command_center_run("gothic platform boots brief")
|
| 74 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 75 |
+
state = _make_base_state(
|
| 76 |
+
generation={"status": "success", "output_path": "/data/artifact.png", "hf_token_present": True},
|
| 77 |
+
)
|
| 78 |
+
|
| 79 |
+
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 80 |
+
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
| 81 |
+
|
| 82 |
+
assert "hf_token_present" not in payload["generation"]
|
| 83 |
+
assert "token" not in json.dumps(payload).lower()
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
def test_export_packet_hackathon_claims_both_success(monkeypatch) -> None:
|
| 87 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 88 |
+
run = build_command_center_run("couture archivist brief")
|
| 89 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 90 |
+
state = _make_base_state(
|
| 91 |
+
minicpm_judge={"status": "success"},
|
| 92 |
+
nemotron_evidence={"status": "success"},
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
result = write_export_packet(run=run, scan=scan, operator_state=state, adult_mode=False)
|
| 96 |
+
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
| 97 |
+
|
| 98 |
+
assert payload["hackathon_claims"]["openbmb_lane"] is True
|
| 99 |
+
assert payload["hackathon_claims"]["nvidia_nemotron_lane"] is True
|
| 100 |
+
assert payload["hackathon_claims"]["gradio_space"] is True
|
| 101 |
+
assert payload["hackathon_claims"]["off_brand_custom_ui"] is True
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def test_export_packet_hackathon_claims_st3gg_export_gate(monkeypatch) -> None:
|
| 105 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 106 |
+
run_clear = build_command_center_run("scan gate test brief clear")
|
| 107 |
+
run_blocked = build_command_center_run("scan gate test brief blocked")
|
| 108 |
+
scan_clear = {"status": "pass", "export_gate": "clear"}
|
| 109 |
+
scan_blocked = {"status": "review", "export_gate": "blocked"}
|
| 110 |
+
|
| 111 |
+
result_clear = write_export_packet(run=run_clear, scan=scan_clear, operator_state=_make_base_state(), adult_mode=False)
|
| 112 |
+
result_blocked = write_export_packet(run=run_blocked, scan=scan_blocked, operator_state=_make_base_state(), adult_mode=False)
|
| 113 |
+
|
| 114 |
+
payload_clear = json.loads(Path(result_clear["path"]).read_text(encoding="utf-8"))
|
| 115 |
+
payload_blocked = json.loads(Path(result_blocked["path"]).read_text(encoding="utf-8"))
|
| 116 |
+
|
| 117 |
+
assert payload_clear["hackathon_claims"]["st3gg_export_gate"] == "clear"
|
| 118 |
+
assert payload_blocked["hackathon_claims"]["st3gg_export_gate"] == "blocked"
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def test_export_packet_includes_model_stack_and_prompts(monkeypatch) -> None:
|
| 122 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 123 |
+
run = build_command_center_run("raven archivist couture brief")
|
| 124 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 125 |
+
|
| 126 |
+
result = write_export_packet(run=run, scan=scan, operator_state=_make_base_state(), adult_mode=False)
|
| 127 |
+
payload = json.loads(Path(result["path"]).read_text(encoding="utf-8"))
|
| 128 |
+
|
| 129 |
+
assert isinstance(payload["model_stack"], list)
|
| 130 |
+
assert len(payload["model_stack"]) > 0
|
| 131 |
+
assert all("repo_id" in entry and "params_b" in entry for entry in payload["model_stack"])
|
| 132 |
+
assert isinstance(payload["prompt"], str)
|
| 133 |
+
assert isinstance(payload["refined_prompt"], str)
|
| 134 |
+
assert isinstance(payload["created_at_epoch"], int)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def test_export_root_uses_nexus_export_dir_env(monkeypatch) -> None:
|
| 138 |
+
custom_dir = Path("outputs/test-exports/custom_export")
|
| 139 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", str(custom_dir))
|
| 140 |
+
|
| 141 |
+
root = export_root()
|
| 142 |
+
|
| 143 |
+
assert root == custom_dir.resolve()
|
| 144 |
+
assert root.is_dir()
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def test_export_root_rejects_src_export_dir(monkeypatch) -> None:
|
| 148 |
+
unsafe_dir = Path("src/nexus_visual_weaver/export-leak")
|
| 149 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", str(unsafe_dir))
|
| 150 |
+
|
| 151 |
+
root = export_root()
|
| 152 |
+
|
| 153 |
+
src_root = (Path.cwd() / "src").resolve()
|
| 154 |
+
assert root != src_root
|
| 155 |
+
assert src_root not in root.parents
|
| 156 |
+
assert not unsafe_dir.exists()
|
| 157 |
+
|
| 158 |
+
|
| 159 |
+
def test_export_root_falls_back_to_outputs_when_no_env(monkeypatch) -> None:
|
| 160 |
+
monkeypatch.delenv("NEXUS_EXPORT_DIR", raising=False)
|
| 161 |
+
# Patch /data so it is not seen as existing
|
| 162 |
+
import nexus_visual_weaver.exporter as exporter_mod
|
| 163 |
+
original_exists = Path.exists
|
| 164 |
+
|
| 165 |
+
def patched_exists(self):
|
| 166 |
+
if str(self) == "/data":
|
| 167 |
+
return False
|
| 168 |
+
return original_exists(self)
|
| 169 |
+
|
| 170 |
+
monkeypatch.setattr(Path, "exists", patched_exists)
|
| 171 |
+
|
| 172 |
+
root = export_root()
|
| 173 |
+
assert "outputs" in str(root) or "exports" in str(root)
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def test_export_packet_returns_both_path_and_packet(monkeypatch) -> None:
|
| 177 |
+
monkeypatch.setenv("NEXUS_EXPORT_DIR", "outputs/test-exports")
|
| 178 |
+
run = build_command_center_run("return structure brief")
|
| 179 |
+
scan = {"status": "pass", "export_gate": "clear"}
|
| 180 |
+
|
| 181 |
+
result = write_export_packet(run=run, scan=scan, operator_state=_make_base_state(), adult_mode=False)
|
| 182 |
+
|
| 183 |
+
assert "path" in result
|
| 184 |
+
assert "packet" in result
|
| 185 |
+
assert isinstance(result["packet"], dict)
|
| 186 |
+
assert result["packet"]["schema"] == "nexus_visual_weaver.export_packet.v1"
|
tests/test_model_relay.py
CHANGED
|
@@ -138,3 +138,79 @@ def test_optional_external_gateways_are_registered_but_excluded_by_default() ->
|
|
| 138 |
assert relay.records["netflix-void-modal"].health == "healthy"
|
| 139 |
assert relay.records["netlify-ai-gateway-helper"].health == "excluded"
|
| 140 |
assert relay.records["fal-media-adapter"].health == "excluded"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
assert relay.records["netflix-void-modal"].health == "healthy"
|
| 139 |
assert relay.records["netlify-ai-gateway-helper"].health == "excluded"
|
| 140 |
assert relay.records["fal-media-adapter"].health == "excluded"
|
| 141 |
+
|
| 142 |
+
|
| 143 |
+
def test_minicpm_v46_is_registered_in_taste_judge_lane() -> None:
|
| 144 |
+
relay = WeaverModelRelay()
|
| 145 |
+
|
| 146 |
+
record = relay.records["minicpm-v46-visual-judge"]
|
| 147 |
+
|
| 148 |
+
assert record.lane == "taste_judge"
|
| 149 |
+
assert record.repo_id == "openbmb/MiniCPM-V-4.6"
|
| 150 |
+
assert record.provider == "openbmb"
|
| 151 |
+
assert record.params_b == 1.30
|
| 152 |
+
assert record.license_gate == "apache-2.0"
|
| 153 |
+
assert record.health == "healthy"
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def test_nemotron_parse_is_registered_in_taste_judge_lane() -> None:
|
| 157 |
+
relay = WeaverModelRelay()
|
| 158 |
+
|
| 159 |
+
record = relay.records["nemotron-parse-v12-evidence"]
|
| 160 |
+
|
| 161 |
+
assert record.lane == "taste_judge"
|
| 162 |
+
assert record.repo_id == "nvidia/NVIDIA-Nemotron-Parse-v1.2"
|
| 163 |
+
assert record.provider == "hf_nvidia"
|
| 164 |
+
assert record.params_b == 0.94
|
| 165 |
+
assert record.health == "healthy"
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_nemotron_nano_is_registered_as_fallback_for_parse() -> None:
|
| 169 |
+
relay = WeaverModelRelay()
|
| 170 |
+
|
| 171 |
+
parse_record = relay.records["nemotron-parse-v12-evidence"]
|
| 172 |
+
nano_record = relay.records["nemotron-nano-4b-gguf-evidence"]
|
| 173 |
+
|
| 174 |
+
assert "nemotron-nano-4b-gguf-evidence" in (parse_record.fallback_chain or ())
|
| 175 |
+
assert nano_record.repo_id == "nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF"
|
| 176 |
+
assert nano_record.lane == "taste_judge"
|
| 177 |
+
assert nano_record.params_b == 3.97
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def test_private_image_research_lane_is_rotatable() -> None:
|
| 181 |
+
from nexus_visual_weaver.model_relay import PINNED_LANES, ROTATABLE_LANES
|
| 182 |
+
|
| 183 |
+
assert "private_image_research" not in PINNED_LANES
|
| 184 |
+
assert "private_image_research" in ROTATABLE_LANES
|
| 185 |
+
|
| 186 |
+
|
| 187 |
+
def test_flux2_klein_4b_is_pinned_with_apache_license() -> None:
|
| 188 |
+
relay = WeaverModelRelay()
|
| 189 |
+
|
| 190 |
+
record = relay.records["flux2-klein-4b-public"]
|
| 191 |
+
|
| 192 |
+
assert record.lane == "image_generation"
|
| 193 |
+
assert record.pinned is True
|
| 194 |
+
assert record.params_b == 4.0
|
| 195 |
+
assert record.license_gate == "apache-2.0"
|
| 196 |
+
assert record.repo_id == "black-forest-labs/FLUX.2-klein-4B"
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def test_flux2_klein_9b_is_not_pinned_and_in_private_research() -> None:
|
| 200 |
+
relay = WeaverModelRelay()
|
| 201 |
+
|
| 202 |
+
record = relay.records["flux2-klein-9b-private"]
|
| 203 |
+
|
| 204 |
+
assert record.lane == "private_image_research"
|
| 205 |
+
assert record.pinned is False
|
| 206 |
+
assert record.params_b == 9.0
|
| 207 |
+
assert record.license_gate == "review_required"
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def test_minicpm_has_fallback_chain_configured() -> None:
|
| 211 |
+
relay = WeaverModelRelay()
|
| 212 |
+
|
| 213 |
+
record = relay.records["minicpm-v46-visual-judge"]
|
| 214 |
+
|
| 215 |
+
assert record.fallback_chain is not None
|
| 216 |
+
assert len(record.fallback_chain) > 0
|
tests/test_provider_runtime.py
CHANGED
|
@@ -1,6 +1,18 @@
|
|
| 1 |
from PIL import Image
|
| 2 |
|
| 3 |
-
from nexus_visual_weaver.provider_runtime import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
|
| 6 |
def test_minicpm_reports_missing_secret(monkeypatch) -> None:
|
|
@@ -70,3 +82,302 @@ def test_nemotron_reports_missing_secret(monkeypatch) -> None:
|
|
| 70 |
assert result.status == "missing_secret"
|
| 71 |
assert result.provider == "NVIDIA"
|
| 72 |
assert "Nemotron" in result.message
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from PIL import Image
|
| 2 |
|
| 3 |
+
from nexus_visual_weaver.provider_runtime import (
|
| 4 |
+
NEMOTRON_NANO_REPO_ID,
|
| 5 |
+
NEMOTRON_PARSE_REPO_ID,
|
| 6 |
+
OPENBMB_REPO_ID,
|
| 7 |
+
ProviderJudgeResult,
|
| 8 |
+
_extract_content,
|
| 9 |
+
_image_data_url,
|
| 10 |
+
_post_json,
|
| 11 |
+
_safe_json_from_text,
|
| 12 |
+
_short_error,
|
| 13 |
+
judge_with_minicpm,
|
| 14 |
+
judge_with_nemotron,
|
| 15 |
+
)
|
| 16 |
|
| 17 |
|
| 18 |
def test_minicpm_reports_missing_secret(monkeypatch) -> None:
|
|
|
|
| 82 |
assert result.status == "missing_secret"
|
| 83 |
assert result.provider == "NVIDIA"
|
| 84 |
assert "Nemotron" in result.message
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
# --- _short_error tests ---
|
| 88 |
+
|
| 89 |
+
def test_short_error_prefixes_class_name() -> None:
|
| 90 |
+
exc = ValueError("something went wrong")
|
| 91 |
+
result = _short_error(exc)
|
| 92 |
+
assert result.startswith("ValueError:")
|
| 93 |
+
assert "something went wrong" in result
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def test_short_error_truncates_long_messages() -> None:
|
| 97 |
+
long_message = "x" * 500
|
| 98 |
+
exc = RuntimeError(long_message)
|
| 99 |
+
result = _short_error(exc)
|
| 100 |
+
assert len(result) <= len("RuntimeError: ") + 360
|
| 101 |
+
assert result.endswith("...")
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def test_short_error_collapses_newlines() -> None:
|
| 105 |
+
exc = OSError("line one\nline two\nline three")
|
| 106 |
+
result = _short_error(exc)
|
| 107 |
+
assert "\n" not in result
|
| 108 |
+
assert "line one" in result
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
# --- _image_data_url tests ---
|
| 112 |
+
|
| 113 |
+
def test_image_data_url_returns_none_for_none_path() -> None:
|
| 114 |
+
assert _image_data_url(None) is None
|
| 115 |
+
|
| 116 |
+
|
| 117 |
+
def test_image_data_url_returns_none_for_missing_file() -> None:
|
| 118 |
+
assert _image_data_url("/nonexistent/path/to/image.png") is None
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
def test_image_data_url_encodes_png_as_base64() -> None:
|
| 122 |
+
image_path = "outputs/test-img-data-url.png"
|
| 123 |
+
Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path)
|
| 124 |
+
|
| 125 |
+
result = _image_data_url(image_path)
|
| 126 |
+
|
| 127 |
+
assert result is not None
|
| 128 |
+
assert result.startswith("data:image/png;base64,")
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def test_image_data_url_uses_jpeg_mime_for_jpg() -> None:
|
| 132 |
+
image_path = "outputs/test-img-data-url.jpg"
|
| 133 |
+
Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path)
|
| 134 |
+
|
| 135 |
+
result = _image_data_url(image_path)
|
| 136 |
+
|
| 137 |
+
assert result is not None
|
| 138 |
+
assert result.startswith("data:image/jpeg;base64,")
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
# --- _extract_content tests ---
|
| 142 |
+
|
| 143 |
+
def test_extract_content_returns_string_from_choices() -> None:
|
| 144 |
+
response = {"choices": [{"message": {"content": "hello world"}}]}
|
| 145 |
+
assert _extract_content(response) == "hello world"
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def test_extract_content_returns_empty_for_no_choices() -> None:
|
| 149 |
+
assert _extract_content({"choices": []}) == ""
|
| 150 |
+
assert _extract_content({}) == ""
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def test_extract_content_serializes_non_string_content() -> None:
|
| 154 |
+
response = {"choices": [{"message": {"content": {"key": "value"}}}]}
|
| 155 |
+
result = _extract_content(response)
|
| 156 |
+
assert "key" in result
|
| 157 |
+
assert "value" in result
|
| 158 |
+
|
| 159 |
+
|
| 160 |
+
# --- _safe_json_from_text tests ---
|
| 161 |
+
|
| 162 |
+
def test_safe_json_from_text_parses_valid_json() -> None:
|
| 163 |
+
text = '{"status": "pass", "score": 0.9}'
|
| 164 |
+
result = _safe_json_from_text(text)
|
| 165 |
+
assert result == {"status": "pass", "score": 0.9}
|
| 166 |
+
|
| 167 |
+
|
| 168 |
+
def test_safe_json_from_text_returns_empty_dict_for_empty_string() -> None:
|
| 169 |
+
assert _safe_json_from_text("") == {}
|
| 170 |
+
assert _safe_json_from_text(" ") == {}
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def test_safe_json_from_text_falls_back_on_invalid_json() -> None:
|
| 174 |
+
text = "this is not valid json"
|
| 175 |
+
result = _safe_json_from_text(text)
|
| 176 |
+
assert "raw_summary" in result
|
| 177 |
+
assert "this is not valid json" in result["raw_summary"]
|
| 178 |
+
|
| 179 |
+
|
| 180 |
+
def test_safe_json_from_text_extracts_embedded_json() -> None:
|
| 181 |
+
text = 'Some prefix text {"result": "ok"} trailing text'
|
| 182 |
+
result = _safe_json_from_text(text)
|
| 183 |
+
assert result == {"result": "ok"}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def test_safe_json_from_text_truncates_fallback_to_1200_chars() -> None:
|
| 187 |
+
long_text = "not json " + "x" * 2000
|
| 188 |
+
result = _safe_json_from_text(long_text)
|
| 189 |
+
assert "raw_summary" in result
|
| 190 |
+
assert len(result["raw_summary"]) <= 1200
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
# --- ProviderJudgeResult.to_dict tests ---
|
| 194 |
+
|
| 195 |
+
def test_provider_judge_result_to_dict_contains_all_fields() -> None:
|
| 196 |
+
result = ProviderJudgeResult(
|
| 197 |
+
status="success",
|
| 198 |
+
provider_state="configured",
|
| 199 |
+
provider="OpenBMB",
|
| 200 |
+
repo_id=OPENBMB_REPO_ID,
|
| 201 |
+
model="MiniCPM-V-4.6",
|
| 202 |
+
message="judge returned evidence",
|
| 203 |
+
evidence={"overall_status": "pass"},
|
| 204 |
+
latency_seconds=1.23,
|
| 205 |
+
)
|
| 206 |
+
d = result.to_dict()
|
| 207 |
+
|
| 208 |
+
assert d["status"] == "success"
|
| 209 |
+
assert d["provider_state"] == "configured"
|
| 210 |
+
assert d["provider"] == "OpenBMB"
|
| 211 |
+
assert d["repo_id"] == OPENBMB_REPO_ID
|
| 212 |
+
assert d["model"] == "MiniCPM-V-4.6"
|
| 213 |
+
assert d["latency_seconds"] == 1.23
|
| 214 |
+
assert d["evidence"]["overall_status"] == "pass"
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
# --- nemotron success/failure tests ---
|
| 218 |
+
|
| 219 |
+
def test_nemotron_success_with_mocked_post(monkeypatch) -> None:
|
| 220 |
+
monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test")
|
| 221 |
+
monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token")
|
| 222 |
+
|
| 223 |
+
def fake_post(url, token, payload, timeout):
|
| 224 |
+
assert "Nemotron" in payload["model"] or "nemotron" in payload["model"]
|
| 225 |
+
return {"choices": [{"message": {"content": '{"final_claim_status":"pass","structured_parse":"ok"}'}}]}
|
| 226 |
+
|
| 227 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 228 |
+
|
| 229 |
+
result = judge_with_nemotron(
|
| 230 |
+
prompt="gothic couture brief",
|
| 231 |
+
run_packet={"checkpoint": {"checkpoint_id": "nw-test-123"}},
|
| 232 |
+
minicpm_result={"status": "success"},
|
| 233 |
+
)
|
| 234 |
+
|
| 235 |
+
assert result.status == "success"
|
| 236 |
+
assert result.provider == "NVIDIA"
|
| 237 |
+
assert result.evidence["final_claim_status"] == "pass"
|
| 238 |
+
assert result.latency_seconds is not None
|
| 239 |
+
|
| 240 |
+
|
| 241 |
+
def test_nemotron_failed_api_call_returns_failed_status(monkeypatch) -> None:
|
| 242 |
+
import urllib.error
|
| 243 |
+
|
| 244 |
+
monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test")
|
| 245 |
+
monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token")
|
| 246 |
+
|
| 247 |
+
def fake_post(url, token, payload, timeout):
|
| 248 |
+
raise urllib.error.URLError("connection refused")
|
| 249 |
+
|
| 250 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 251 |
+
|
| 252 |
+
result = judge_with_nemotron(
|
| 253 |
+
prompt="brief",
|
| 254 |
+
run_packet={"id": "nw-fail"},
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
assert result.status == "failed"
|
| 258 |
+
assert result.provider_state == "failed"
|
| 259 |
+
assert "URLError" in result.message or "connection" in result.message.lower()
|
| 260 |
+
assert result.evidence.get("configured") is True
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_minicpm_failed_api_call_returns_failed_status(monkeypatch) -> None:
|
| 264 |
+
import urllib.error
|
| 265 |
+
|
| 266 |
+
image_path = "outputs/test-minicpm-fail.png"
|
| 267 |
+
Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path)
|
| 268 |
+
monkeypatch.setenv("MINICPM_BASE_URL", "http://minicpm.test")
|
| 269 |
+
monkeypatch.setenv("MINICPM_API_KEY", "test-token")
|
| 270 |
+
|
| 271 |
+
def fake_post(url, token, payload, timeout):
|
| 272 |
+
raise urllib.error.URLError("network unreachable")
|
| 273 |
+
|
| 274 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 275 |
+
|
| 276 |
+
result = judge_with_minicpm(
|
| 277 |
+
prompt="gothic couture",
|
| 278 |
+
image_path=image_path,
|
| 279 |
+
scan={"export_gate": "clear"},
|
| 280 |
+
wardrobe_summary="platform boots",
|
| 281 |
+
)
|
| 282 |
+
|
| 283 |
+
assert result.status == "failed"
|
| 284 |
+
assert result.provider_state == "failed"
|
| 285 |
+
assert result.provider == "OpenBMB"
|
| 286 |
+
assert result.evidence.get("configured") is True
|
| 287 |
+
|
| 288 |
+
|
| 289 |
+
def test_nemotron_uses_parse_repo_id_for_parse_model(monkeypatch) -> None:
|
| 290 |
+
monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test")
|
| 291 |
+
monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token")
|
| 292 |
+
monkeypatch.setenv("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-Parse-v1.2")
|
| 293 |
+
|
| 294 |
+
def fake_post(url, token, payload, timeout):
|
| 295 |
+
return {"choices": [{"message": {"content": "{}"}}]}
|
| 296 |
+
|
| 297 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 298 |
+
|
| 299 |
+
result = judge_with_nemotron(prompt="brief", run_packet={})
|
| 300 |
+
|
| 301 |
+
assert result.repo_id == NEMOTRON_PARSE_REPO_ID
|
| 302 |
+
|
| 303 |
+
|
| 304 |
+
def test_nemotron_uses_nano_repo_id_for_non_parse_model(monkeypatch) -> None:
|
| 305 |
+
monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test")
|
| 306 |
+
monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token")
|
| 307 |
+
monkeypatch.setenv("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF")
|
| 308 |
+
|
| 309 |
+
def fake_post(url, token, payload, timeout):
|
| 310 |
+
return {"choices": [{"message": {"content": "{}"}}]}
|
| 311 |
+
|
| 312 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 313 |
+
|
| 314 |
+
result = judge_with_nemotron(prompt="brief", run_packet={})
|
| 315 |
+
|
| 316 |
+
assert result.repo_id == NEMOTRON_NANO_REPO_ID
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
def test_minicpm_uses_openbmb_api_key_as_fallback(monkeypatch) -> None:
|
| 320 |
+
monkeypatch.setenv("MINICPM_BASE_URL", "http://minicpm.test")
|
| 321 |
+
monkeypatch.delenv("MINICPM_API_KEY", raising=False)
|
| 322 |
+
monkeypatch.setenv("OPENBMB_API_KEY", "openbmb-fallback-token")
|
| 323 |
+
|
| 324 |
+
image_path = "outputs/test-openbmb-key.png"
|
| 325 |
+
Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path)
|
| 326 |
+
|
| 327 |
+
captured = {}
|
| 328 |
+
|
| 329 |
+
def fake_post(url, token, payload, timeout):
|
| 330 |
+
captured["token"] = token
|
| 331 |
+
return {"choices": [{"message": {"content": '{"status": "ok"}'}}]}
|
| 332 |
+
|
| 333 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 334 |
+
|
| 335 |
+
result = judge_with_minicpm(
|
| 336 |
+
prompt="test",
|
| 337 |
+
image_path=image_path,
|
| 338 |
+
scan={},
|
| 339 |
+
wardrobe_summary="",
|
| 340 |
+
)
|
| 341 |
+
|
| 342 |
+
assert result.status == "success"
|
| 343 |
+
assert captured["token"] == "openbmb-fallback-token"
|
| 344 |
+
|
| 345 |
+
|
| 346 |
+
def test_nemotron_uses_nvidia_api_key_as_fallback(monkeypatch) -> None:
|
| 347 |
+
monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test")
|
| 348 |
+
monkeypatch.delenv("NEMOTRON_API_KEY", raising=False)
|
| 349 |
+
monkeypatch.setenv("NVIDIA_API_KEY", "nvidia-fallback-token")
|
| 350 |
+
|
| 351 |
+
captured = {}
|
| 352 |
+
|
| 353 |
+
def fake_post(url, token, payload, timeout):
|
| 354 |
+
captured["token"] = token
|
| 355 |
+
return {"choices": [{"message": {"content": "{}"}}]}
|
| 356 |
+
|
| 357 |
+
monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post)
|
| 358 |
+
|
| 359 |
+
result = judge_with_nemotron(prompt="brief", run_packet={})
|
| 360 |
+
|
| 361 |
+
assert result.status == "success"
|
| 362 |
+
assert captured["token"] == "nvidia-fallback-token"
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def test_post_json_rejects_unsupported_url_schemes_before_urlopen(monkeypatch) -> None:
|
| 366 |
+
called = False
|
| 367 |
+
|
| 368 |
+
def fake_urlopen(*args, **kwargs):
|
| 369 |
+
nonlocal called
|
| 370 |
+
called = True
|
| 371 |
+
raise AssertionError("urlopen should not be called for invalid schemes")
|
| 372 |
+
|
| 373 |
+
monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)
|
| 374 |
+
|
| 375 |
+
for url in ["file:///tmp/payload.json", "ftp://example.test/api", "http:///missing-host"]:
|
| 376 |
+
try:
|
| 377 |
+
_post_json(url, "token", {"ok": True}, 1.0)
|
| 378 |
+
except ValueError as exc:
|
| 379 |
+
assert "Invalid URL" in str(exc)
|
| 380 |
+
else:
|
| 381 |
+
raise AssertionError(f"{url} should have been rejected")
|
| 382 |
+
|
| 383 |
+
assert called is False
|