| from PIL import Image |
| from pathlib import Path |
|
|
| from nexus_visual_weaver.provider_runtime import ( |
| NEMOTRON_NANO_REPO_ID, |
| NEMOTRON_PARSE_REPO_ID, |
| OPENBMB_REPO_ID, |
| ProviderJudgeResult, |
| _extract_content, |
| _image_data_url, |
| _post_json, |
| _safe_json_from_text, |
| _safe_provider_payload, |
| _short_error, |
| judge_with_minicpm, |
| judge_with_nemotron, |
| ) |
|
|
|
|
| RUNTIME_FIXTURE_DIR = Path("tests/fixtures/runtime") |
|
|
|
|
| def _runtime_fixture_path(name: str) -> Path: |
| RUNTIME_FIXTURE_DIR.mkdir(parents=True, exist_ok=True) |
| return RUNTIME_FIXTURE_DIR / name |
|
|
|
|
| def test_minicpm_reports_missing_secret(monkeypatch) -> None: |
| monkeypatch.delenv("MINICPM_BASE_URL", raising=False) |
| monkeypatch.delenv("MINICPM_API_KEY", raising=False) |
| monkeypatch.delenv("OPENBMB_API_KEY", raising=False) |
|
|
| result = judge_with_minicpm( |
| prompt="gothic couture", |
| image_path=None, |
| scan={"export_gate": "pending"}, |
| wardrobe_summary="boots and lace", |
| ) |
|
|
| assert result.status == "missing_secret" |
| assert result.provider_state == "missing secret" |
| assert result.repo_id == "openbmb/MiniCPM-V-4.6" |
|
|
|
|
| def test_minicpm_blocks_when_artifact_missing(monkeypatch) -> None: |
| monkeypatch.setenv("MINICPM_BASE_URL", "http://127.0.0.1:9") |
| monkeypatch.setenv("MINICPM_API_KEY", "test-token") |
|
|
| result = judge_with_minicpm( |
| prompt="gothic couture", |
| image_path="missing.png", |
| scan={"export_gate": "clear"}, |
| wardrobe_summary="boots and lace", |
| ) |
|
|
| assert result.status == "no_artifact" |
| assert result.provider_state == "blocked" |
|
|
|
|
| def test_minicpm_success_with_mocked_post(monkeypatch) -> None: |
| image = _runtime_fixture_path("test-provider-artifact.png") |
| Image.new("RGB", (8, 8), color=(12, 16, 20)).save(image) |
| monkeypatch.setenv("MINICPM_BASE_URL", "http://example.test") |
| monkeypatch.setenv("MINICPM_API_KEY", "test-token") |
|
|
| def fake_post(url, token, payload, timeout): |
| assert url == "http://example.test/v1/chat/completions" |
| assert token == "test-token" |
| assert payload["messages"][0]["content"][1]["image_url"]["url"].startswith("data:image/png;base64,") |
| return {"choices": [{"message": {"content": '{"overall_status":"pass","footwear_check":"visible"}'}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| try: |
| result = judge_with_minicpm( |
| prompt="gothic couture", |
| image_path=str(image), |
| scan={"export_gate": "clear"}, |
| wardrobe_summary="boots and lace", |
| ) |
| finally: |
| image.unlink(missing_ok=True) |
|
|
| assert result.status == "success" |
| assert result.evidence["overall_status"] == "pass" |
|
|
|
|
| def test_nemotron_reports_missing_secret(monkeypatch) -> None: |
| monkeypatch.delenv("NEMOTRON_BASE_URL", raising=False) |
| monkeypatch.delenv("NEMOTRON_API_KEY", raising=False) |
| monkeypatch.delenv("NVIDIA_API_KEY", raising=False) |
|
|
| result = judge_with_nemotron(prompt="brief", run_packet={"id": "nw-test"}) |
|
|
| assert result.status == "missing_secret" |
| assert result.provider == "NVIDIA" |
| assert "Nemotron" in result.message |
|
|
|
|
| |
|
|
| def test_short_error_prefixes_class_name() -> None: |
| exc = ValueError("something went wrong") |
| result = _short_error(exc) |
| assert result.startswith("ValueError:") |
| assert "something went wrong" in result |
|
|
|
|
| def test_short_error_truncates_long_messages() -> None: |
| long_message = "x" * 500 |
| exc = RuntimeError(long_message) |
| result = _short_error(exc) |
| assert len(result) <= len("RuntimeError: ") + 360 |
| assert result.endswith("...") |
|
|
|
|
| def test_short_error_collapses_newlines() -> None: |
| exc = OSError("line one\nline two\nline three") |
| result = _short_error(exc) |
| assert "\n" not in result |
| assert "line one" in result |
|
|
|
|
| |
|
|
| def test_image_data_url_returns_none_for_none_path() -> None: |
| assert _image_data_url(None) is None |
|
|
|
|
| def test_image_data_url_returns_none_for_missing_file() -> None: |
| assert _image_data_url("/nonexistent/path/to/image.png") is None |
|
|
|
|
| def test_image_data_url_encodes_png_as_base64() -> None: |
| image_path = _runtime_fixture_path("test-img-data-url.png") |
| Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path) |
|
|
| try: |
| result = _image_data_url(str(image_path)) |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
| assert result is not None |
| assert result.startswith("data:image/png;base64,") |
|
|
|
|
| def test_image_data_url_uses_jpeg_mime_for_jpg() -> None: |
| image_path = _runtime_fixture_path("test-img-data-url.jpg") |
| Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path) |
|
|
| try: |
| result = _image_data_url(str(image_path)) |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
| assert result is not None |
| assert result.startswith("data:image/jpeg;base64,") |
|
|
|
|
| def test_image_data_url_rejects_unknown_suffix() -> None: |
| image_path = _runtime_fixture_path("not-an-image.txt") |
| image_path.write_text("not an image", encoding="utf-8") |
|
|
| try: |
| assert _image_data_url(str(image_path)) is None |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
|
|
| def test_image_data_url_rejects_oversized_file(monkeypatch) -> None: |
| image_path = _runtime_fixture_path("large.png") |
| image_path.write_bytes(b"not actually decoded because size fails") |
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime.MAX_PROVIDER_IMAGE_BYTES", 4) |
|
|
| try: |
| assert _image_data_url(str(image_path)) is None |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
|
|
| |
|
|
| def test_extract_content_returns_string_from_choices() -> None: |
| response = {"choices": [{"message": {"content": "hello world"}}]} |
| assert _extract_content(response) == "hello world" |
|
|
|
|
| def test_extract_content_returns_empty_for_no_choices() -> None: |
| assert _extract_content({"choices": []}) == "" |
| assert _extract_content({}) == "" |
|
|
|
|
| def test_extract_content_serializes_non_string_content() -> None: |
| response = {"choices": [{"message": {"content": {"key": "value"}}}]} |
| result = _extract_content(response) |
| assert "key" in result |
| assert "value" in result |
|
|
|
|
| |
|
|
| def test_safe_json_from_text_parses_valid_json() -> None: |
| text = '{"status": "pass", "score": 0.9}' |
| result = _safe_json_from_text(text) |
| assert result == {"status": "pass", "score": 0.9} |
|
|
|
|
| def test_safe_json_from_text_returns_empty_dict_for_empty_string() -> None: |
| assert _safe_json_from_text("") == {} |
| assert _safe_json_from_text(" ") == {} |
|
|
|
|
| def test_safe_json_from_text_falls_back_on_invalid_json() -> None: |
| text = "this is not valid json" |
| result = _safe_json_from_text(text) |
| assert "raw_summary" in result |
| assert "this is not valid json" in result["raw_summary"] |
|
|
|
|
| def test_safe_json_from_text_extracts_embedded_json() -> None: |
| text = 'Some prefix text {"result": "ok"} trailing text' |
| result = _safe_json_from_text(text) |
| assert result == {"result": "ok"} |
|
|
|
|
| def test_safe_json_from_text_truncates_fallback_to_1200_chars() -> None: |
| long_text = "not json " + "x" * 2000 |
| result = _safe_json_from_text(long_text) |
| assert "raw_summary" in result |
| assert len(result["raw_summary"]) <= 1200 |
|
|
|
|
| |
|
|
| def test_provider_judge_result_to_dict_contains_all_fields() -> None: |
| result = ProviderJudgeResult( |
| status="success", |
| provider_state="configured", |
| provider="OpenBMB", |
| repo_id=OPENBMB_REPO_ID, |
| model="MiniCPM-V-4.6", |
| message="judge returned evidence", |
| evidence={"overall_status": "pass"}, |
| latency_seconds=1.23, |
| ) |
| d = result.to_dict() |
|
|
| assert d["status"] == "success" |
| assert d["provider_state"] == "configured" |
| assert d["provider"] == "OpenBMB" |
| assert d["repo_id"] == OPENBMB_REPO_ID |
| assert d["model"] == "MiniCPM-V-4.6" |
| assert d["latency_seconds"] == 1.23 |
| assert d["evidence"]["overall_status"] == "pass" |
|
|
|
|
| |
|
|
| def test_nemotron_success_with_mocked_post(monkeypatch) -> None: |
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test") |
| monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token") |
|
|
| def fake_post(url, token, payload, timeout): |
| assert "Nemotron" in payload["model"] or "nemotron" in payload["model"] |
| return {"choices": [{"message": {"content": '{"final_claim_status":"pass","structured_parse":"ok"}'}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron( |
| prompt="gothic couture brief", |
| run_packet={"checkpoint": {"checkpoint_id": "nw-test-123"}}, |
| minicpm_result={"status": "success"}, |
| ) |
|
|
| assert result.status == "success" |
| assert result.provider == "NVIDIA" |
| assert result.evidence["final_claim_status"] == "pass" |
| assert result.latency_seconds is not None |
|
|
|
|
| def test_nemotron_failed_api_call_returns_failed_status(monkeypatch) -> None: |
| import urllib.error |
|
|
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test") |
| monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token") |
|
|
| def fake_post(url, token, payload, timeout): |
| raise urllib.error.URLError("connection refused") |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron( |
| prompt="brief", |
| run_packet={"id": "nw-fail"}, |
| ) |
|
|
| assert result.status == "failed" |
| assert result.provider_state == "failed" |
| assert "URLError" in result.message or "connection" in result.message.lower() |
| assert result.evidence.get("configured") is True |
|
|
|
|
| def test_minicpm_failed_api_call_returns_failed_status(monkeypatch) -> None: |
| import urllib.error |
|
|
| image_path = _runtime_fixture_path("test-minicpm-fail.png") |
| Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path) |
| monkeypatch.setenv("MINICPM_BASE_URL", "http://minicpm.test") |
| monkeypatch.setenv("MINICPM_API_KEY", "test-token") |
|
|
| def fake_post(url, token, payload, timeout): |
| raise urllib.error.URLError("network unreachable") |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| try: |
| result = judge_with_minicpm( |
| prompt="gothic couture", |
| image_path=str(image_path), |
| scan={"export_gate": "clear"}, |
| wardrobe_summary="platform boots", |
| ) |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
| assert result.status == "failed" |
| assert result.provider_state == "failed" |
| assert result.provider == "OpenBMB" |
| assert result.evidence.get("configured") is True |
|
|
|
|
| def test_nemotron_uses_parse_repo_id_for_parse_model(monkeypatch) -> None: |
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test") |
| monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token") |
| monkeypatch.setenv("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-Parse-v1.2") |
|
|
| def fake_post(url, token, payload, timeout): |
| return {"choices": [{"message": {"content": "{}"}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron(prompt="brief", run_packet={}) |
|
|
| assert result.repo_id == NEMOTRON_PARSE_REPO_ID |
|
|
|
|
| def test_nemotron_uses_nano_repo_id_for_non_parse_model(monkeypatch) -> None: |
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test") |
| monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token") |
| monkeypatch.setenv("NEMOTRON_MODEL", "nvidia/NVIDIA-Nemotron-3-Nano-4B-GGUF") |
|
|
| def fake_post(url, token, payload, timeout): |
| return {"choices": [{"message": {"content": "{}"}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron(prompt="brief", run_packet={}) |
|
|
| assert result.repo_id == NEMOTRON_NANO_REPO_ID |
|
|
|
|
| def test_minicpm_uses_openbmb_api_key_as_fallback(monkeypatch) -> None: |
| monkeypatch.setenv("MINICPM_BASE_URL", "http://minicpm.test") |
| monkeypatch.delenv("MINICPM_API_KEY", raising=False) |
| monkeypatch.setenv("OPENBMB_API_KEY", "openbmb-fallback-token") |
|
|
| image_path = _runtime_fixture_path("test-openbmb-key.png") |
| Image.new("RGB", (4, 4), color=(0, 0, 0)).save(image_path) |
|
|
| captured = {} |
|
|
| def fake_post(url, token, payload, timeout): |
| captured["token"] = token |
| return {"choices": [{"message": {"content": '{"status": "ok"}'}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| try: |
| result = judge_with_minicpm( |
| prompt="test", |
| image_path=str(image_path), |
| scan={}, |
| wardrobe_summary="", |
| ) |
| finally: |
| image_path.unlink(missing_ok=True) |
|
|
| assert result.status == "success" |
| assert captured["token"] == "openbmb-fallback-token" |
|
|
|
|
| def test_nemotron_uses_nvidia_api_key_as_fallback(monkeypatch) -> None: |
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://nemotron.test") |
| monkeypatch.delenv("NEMOTRON_API_KEY", raising=False) |
| monkeypatch.setenv("NVIDIA_API_KEY", "nvidia-fallback-token") |
|
|
| captured = {} |
|
|
| def fake_post(url, token, payload, timeout): |
| captured["token"] = token |
| return {"choices": [{"message": {"content": "{}"}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron(prompt="brief", run_packet={}) |
|
|
| assert result.status == "success" |
| assert captured["token"] == "nvidia-fallback-token" |
|
|
|
|
| def test_safe_provider_payload_redacts_sensitive_nested_keys() -> None: |
| result = _safe_provider_payload( |
| { |
| "checkpoint": "ok", |
| "HF_TOKEN": "should-not-leak", |
| "nested": {"raw_payload": "hidden", "items": [{"base64_image": "hidden"}, {"safe": "visible"}]}, |
| } |
| ) |
|
|
| assert result["checkpoint"] == "ok" |
| assert result["HF_TOKEN"] == "[redacted]" |
| assert result["nested"]["raw_payload"] == "[redacted]" |
| assert result["nested"]["items"][0]["base64_image"] == "[redacted]" |
| assert result["nested"]["items"][1]["safe"] == "visible" |
|
|
|
|
| def test_nemotron_redacts_run_packet_before_provider_call(monkeypatch) -> None: |
| monkeypatch.setenv("NEMOTRON_BASE_URL", "http://localhost:8001") |
| monkeypatch.setenv("NEMOTRON_API_KEY", "nvidia-token") |
| captured = {} |
|
|
| def fake_post(url, token, payload, timeout): |
| captured["content"] = payload["messages"][0]["content"] |
| return {"choices": [{"message": {"content": '{"final_claim_status":"pass"}'}}]} |
|
|
| monkeypatch.setattr("nexus_visual_weaver.provider_runtime._post_json", fake_post) |
|
|
| result = judge_with_nemotron( |
| prompt="brief", |
| run_packet={"safe": "visible", "api_key": "hidden-key", "nested": {"payload_bytes": "hidden-bytes"}}, |
| minicpm_result={"status": "success", "authorization": "hidden-auth"}, |
| ) |
|
|
| assert result.status == "success" |
| assert "visible" in captured["content"] |
| assert "hidden-key" not in captured["content"] |
| assert "hidden-bytes" not in captured["content"] |
| assert "hidden-auth" not in captured["content"] |
| assert "[redacted]" in captured["content"] |
|
|
|
|
| def test_post_json_rejects_unsupported_url_schemes_before_urlopen(monkeypatch) -> None: |
| called = False |
|
|
| def fake_urlopen(*args, **kwargs): |
| nonlocal called |
| called = True |
| raise AssertionError("urlopen should not be called for invalid schemes") |
|
|
| monkeypatch.setattr("urllib.request.urlopen", fake_urlopen) |
|
|
| for url in ["file:///tmp/payload.json", "ftp://example.test/api", "http:///missing-host"]: |
| try: |
| _post_json(url, "token", {"ok": True}, 1.0) |
| except ValueError as exc: |
| assert "Invalid URL" in str(exc) |
| else: |
| raise AssertionError(f"{url} should have been rejected") |
|
|
| assert called is False |
|
|
|
|
| def test_post_json_rejects_plain_http_non_loopback_before_urlopen(monkeypatch) -> None: |
| called = False |
|
|
| def fake_urlopen(*args, **kwargs): |
| nonlocal called |
| called = True |
| raise AssertionError("urlopen should not be called for plaintext remote provider URLs") |
|
|
| monkeypatch.setattr("urllib.request.urlopen", fake_urlopen) |
|
|
| try: |
| _post_json("http://example.test/v1/chat/completions", "token", {"ok": True}, 1.0) |
| except ValueError as exc: |
| assert "HTTPS" in str(exc) |
| assert "loopback" in str(exc) |
| else: |
| raise AssertionError("remote http provider URL should have been rejected") |
|
|
| assert called is False |
|
|
|
|
| def test_post_json_allows_loopback_http_for_local_tests(monkeypatch) -> None: |
| class FakeResponse: |
| def __enter__(self): |
| return self |
|
|
| def __exit__(self, exc_type, exc, tb): |
| return False |
|
|
| def read(self): |
| return b'{"ok": true}' |
|
|
| def fake_urlopen(request, timeout): |
| assert request.full_url == "http://127.0.0.1:8000/v1/chat/completions" |
| return FakeResponse() |
|
|
| monkeypatch.setattr("urllib.request.urlopen", fake_urlopen) |
|
|
| assert _post_json("http://127.0.0.1:8000/v1/chat/completions", "token", {"ok": True}, 1.0) == {"ok": True} |
|
|