Files
Claude 97b85a3f09 Interpolate: RTX Frame Generation as its own module and add-on, button and modes like Upscale
Shared video stream and NVIDIA helpers move to core (video_pipe, nvidia); the add-on needs nvidia-vfx 0.2+, moves an older one up and never moves a newer one back; Upscale and Interpolate cannot feed each other in Always mode.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tw9fjW75aYB8UyVRZo6fCr
2026-10-29 13:00:01 +00:00

179 lines
8.0 KiB
Python

"""Add-ons: what is offered, what a switch does, and what the next start installs."""
import asyncio
import json
import pytest
from core import addons, config, update
@pytest.fixture
def here(tmp_path, monkeypatch):
"""addons.json in a temp folder; `present` is the set of add-on ids whose packages 'are installed'."""
monkeypatch.setattr(config, "ADDONS_FILE", tmp_path / "addons.json")
present = set()
monkeypatch.setattr(addons, "_present", lambda a: a["id"] in present)
for a in addons.ADDONS:
monkeypatch.setitem(a, "offered", lambda: True)
return present
def state(aid):
return next(a["state"] for a in addons.listing() if a["id"] == aid)
def test_nothing_is_wanted_until_asked(here):
assert state("nvidia_vsr") == "available" and state("camera") == "available"
assert not addons.enabled("camera")
def test_what_was_installed_before_add_ons_existed_stays_on(here):
here.add("camera")
assert state("camera") == "installed" and addons.enabled("camera")
def test_switching_on_waits_for_the_restart_and_off_keeps_the_packages(here):
assert addons.set_wanted("nvidia_vsr", True)
assert state("nvidia_vsr") == "pending" and not addons.enabled("nvidia_vsr")
here.add("nvidia_vsr") # the restart installed it
assert state("nvidia_vsr") == "installed" and addons.enabled("nvidia_vsr")
addons.set_wanted("nvidia_vsr", False)
assert state("nvidia_vsr") == "disabled" and not addons.enabled("nvidia_vsr")
def test_an_add_on_this_machine_cannot_run_is_not_offered(here, monkeypatch):
for gone in ("nvidia_vsr", "nvidia_framegen"):
monkeypatch.setitem(addons._BY_ID[gone], "offered", lambda: False)
assert [a["id"] for a in addons.listing()] == ["camera", "lora_training"]
assert addons.set_wanted("nvidia_vsr", True) is False
assert addons.set_wanted("no-such-addon", True) is False
def test_the_start_installs_only_what_is_wanted_and_missing(here, monkeypatch):
ran = []
def fake(lines, upgrade=()):
ran.append(list(lines))
here.add("nvidia_vsr")
return {"ok": True, "detail": ""}
monkeypatch.setattr(update, "install_lines", fake)
addons.install_wanted()
assert ran == [], "installed something nobody asked for"
addons.set_wanted("nvidia_vsr", True)
addons.install_wanted()
assert ran == [["nvidia-vfx>=0.2"]]
addons.install_wanted()
assert len(ran) == 1, "installed again though it is already here"
def test_a_failed_install_is_said_and_not_retried_until_switched_off_and_on(here, monkeypatch):
ran = []
monkeypatch.setattr(update, "install_lines", lambda lines, upgrade=(): ran.append(1) or {"ok": False, "detail": "no wheel for this Python"})
addons.set_wanted("nvidia_vsr", True)
addons.install_wanted()
row = next(a for a in addons.listing() if a["id"] == "nvidia_vsr")
assert row["failed"] == "no wheel for this Python" and row["state"] == "available", "a failed install must not read as waiting for a restart"
addons.install_wanted()
assert len(ran) == 1, "a broken install would be retried on every start"
addons.set_wanted("nvidia_vsr", False)
addons.set_wanted("nvidia_vsr", True)
addons.install_wanted()
assert len(ran) == 2
def test_install_lines_skips_what_is_present_and_moves_nothing(monkeypatch):
seen = {}
monkeypatch.setattr(update, "_installed_version", lambda n: "1" if n == "gguf" else None)
monkeypatch.setattr(update, "_pip_freeze", lambda: {"torch": "2.10.0"})
monkeypatch.setattr(update, "_install_each", lambda status, path, before, note, timeout, broken:
seen.update(lines=status["install"], pins=open(path).read()) or {"ok": True})
update.install_lines(["gguf", "nvidia-vfx>=0.2"])
assert seen["lines"] == ["nvidia-vfx>=0.2"]
assert "torch==2.10.0" in seen["pins"]
def test_the_routes_list_and_switch(comfyui, here):
from aiohttp import web
from aiohttp.test_utils import TestClient, TestServer
from core import routes
async def go():
app, table = web.Application(), web.RouteTableDef()
routes.register(table, prefix="/fun")
app.add_routes(table)
async with TestClient(TestServer(app)) as client:
got = await (await client.get("/fun/api/addons")).json()
bad = await client.post("/fun/api/addons", json={"id": "camera", "enabled": "yes"})
ok = await (await client.post("/fun/api/addons", json={"id": "camera", "enabled": True})).json()
return got, bad.status, ok
got, bad, ok = asyncio.run(go())
assert {a["id"] for a in got["addons"]} == {"nvidia_vsr", "nvidia_framegen", "camera", "lora_training"}
assert bad == 400
assert next(a["state"] for a in ok["addons"] if a["id"] == "camera") == "pending"
assert json.loads(config.ADDONS_FILE.read_text())["wanted"] == ["camera"]
def test_an_add_on_this_machine_cannot_run_is_not_enabled_even_if_wanted_and_installed(here, monkeypatch):
here.add("nvidia_vsr")
addons.set_wanted("nvidia_vsr", True)
assert addons.enabled("nvidia_vsr")
monkeypatch.setitem(addons._BY_ID["nvidia_vsr"], "offered", lambda: False) # the folder moved to a machine without the card
assert not addons.enabled("nvidia_vsr")
def test_lora_training_starts_on_when_its_package_is_already_there(here):
here.add("lora_training")
assert addons.enabled("lora_training")
assert next(a["state"] for a in addons.listing() if a["id"] == "lora_training") == "installed"
addons.set_wanted("lora_training", False)
assert not addons.enabled("lora_training")
def test_the_camera_add_on_pins_the_preshed_that_can_load_spacy_models():
camera = next(a for a in addons.ADDONS if a["id"] == "camera")
assert addons.PRESHED_PIN in camera["requires"]
def test_preshed_3_0_14_is_flagged_and_older_is_not(monkeypatch):
from importlib import metadata
for have, bad in (("3.0.14", True), ("3.0.13", False), ("3.1.0", True)):
monkeypatch.setattr(metadata, "version", lambda _n, have=have: have)
assert addons.preshed_broken() is bad
def test_frame_generation_is_a_separate_switch_on_the_same_package():
vsr, fg = addons._BY_ID["nvidia_vsr"], addons._BY_ID["nvidia_framegen"]
assert vsr["requires"] == fg["requires"] == ("nvidia-vfx>=0.2",) # neither add-on can ask for the 0.1 that has no frame generation
assert fg["floors"] == {"nvidia-vfx": "0.2"} and "floors" not in vsr
def test_an_old_nvidia_vfx_is_present_for_super_resolution_but_not_for_frame_generation(monkeypatch):
monkeypatch.setattr(addons.importlib.util, "find_spec", lambda m: object())
for have, ok in (("0.1.0.1", False), ("0.2.0.0", True), ("0.10.0", True)):
monkeypatch.setattr(update, "_installed_version", lambda n, have=have: have)
assert addons._present(addons._BY_ID["nvidia_framegen"]) is ok
assert addons._present(addons._BY_ID["nvidia_vsr"]) is True
def _pip(monkeypatch, installed, freeze):
seen = {}
monkeypatch.setattr(update, "_installed_version", lambda n: installed.get(n))
monkeypatch.setattr(update, "_pip_freeze", lambda: freeze)
monkeypatch.setattr(update, "_install_each", lambda status, path, before, note, timeout, broken:
seen.update(lines=status["install"], pins=open(path).read()) or {"ok": True})
return seen
def test_frame_generation_moves_an_old_nvidia_vfx_up_and_nothing_else(monkeypatch):
seen = _pip(monkeypatch, {"nvidia-vfx": "0.1.0.1"}, {"torch": "2.10.0", "nvidia-vfx": "0.1.0.1"})
update.install_lines(["nvidia-vfx>=0.2"], upgrade=("nvidia-vfx",))
assert seen["lines"] == ["nvidia-vfx>=0.2"]
assert "torch==2.10.0" in seen["pins"] and "nvidia-vfx" not in seen["pins"]
def test_super_resolution_never_moves_a_newer_nvidia_vfx_back(monkeypatch):
seen = _pip(monkeypatch, {"nvidia-vfx": "0.2.0.0"}, {"nvidia-vfx": "0.2.0.0"})
assert update.install_lines(["nvidia-vfx>=0.2"])["ran"] is False and not seen # present: nothing to do, in either order
assert update.install_lines(["nvidia-vfx>=0.2"], upgrade=("nvidia-vfx",))["ran"] is False # already at the floor