Pass keep_alive="0s" on every Ollama generate call so models are
evicted from GPU VRAM immediately instead of lingering 5 minutes,
which caused CUDA OOM when downstream diffusion models loaded.
Each node now runs async VRAM cleanup in a try/finally block. The
cleanup uses unload=True so it also evicts a model loaded via the
subprocess fallback path (which carries the default keep_alive).
Add unload_model(), release_vram(), cleanup() and cleanup_async()
to OllamaClient, plus a per-node unload toggle on PromptRefiner.
Require a colon separator and anchor the Negative label to a line
start so the word 'negative' in the positive prompt body (e.g. the
art term 'negative space') is no longer mistaken for a stream label.
Add regression tests.
Add PromptDualStreamRefinerNode that produces a positive and negative
prompt pair in a single pass via Ollama, intended for the shipped Q8
GGUF of qwen2-5-7b-dual-stream-prompt-lora. Reuses OllamaClient for
streaming, timeouts, progress, and llama-runner crash handling, and
parses Positive/Negative output defensively across label variants.
Includes config/Modelfile.dualstream, unit tests, node registration,
and a corrected implementation plan replacing the invalid local
transformers/PEFT approach.
Replace the catch-all RuntimeError wrapper in OllamaClient.generate_streaming
with a StreamResult dataclass that categorises failures (ok, timeout, transient,
model_crash, server_error, unavailable). Llama-runner crashes (HTTP 500 with
"runner terminated" / "exit status" / "load failed") are detected and surfaced
as user-actionable messages in the ComfyUI prompt output instead of bubbling
up as Python stacktraces.
PromptGenerator, NegativePrompt, and PromptRefiner nodes now branch on
result.kind: model_crash / server_error / unavailable surface the message
directly (subprocess fallback would also fail), while timeout / transient
fall through to the existing subprocess fallback path.
Adds 6 unit tests covering each error class plus the success path. Updates
the existing PromptRefiner seed tests to return StreamResult from mocks.
Direct response to the v1.1.6 packaging gap, where the prompt-chain nodes
(combiner, refiner, negative) were authored under nodes/ but never wired
into NODE_CLASS_MAPPINGS, so they silently failed to ship.
- __init__.py now raises RuntimeError at import time if NODE_CLASS_MAPPINGS
and NODE_DISPLAY_NAME_MAPPINGS drift apart. ComfyUI startup logs surface
the mismatch instead of dropping the node from the menu silently.
- New tests/unit/test_node_registration.py contract suite (8 tests):
* Expected 5 keys are registered.
* Display names cover every class.
* Each registered class satisfies the ComfyUI node interface
(INPUT_TYPES classmethod, RETURN_TYPES, FUNCTION, CATEGORY,
and the FUNCTION attribute resolves to a callable method).
* Every nodes/*_node.py module on disk is reachable from the mapping —
this is the regression guard that would have caught v1.1.6.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Changes:
- Add [tool.ruff] config with target-version py310 and a curated rule set
(E/F/W, I, UP, B, SIM, RUF) so future drift is caught in CI lint.
- PEP 585 sweep across all node modules: drop legacy typing.Dict / List /
Tuple / Optional in favor of dict / list / tuple / `X | None`. Annotate
class-level mutable defaults as ClassVar to satisfy RUF012.
- PromptCombinerNode: replace the magic-string mode chain with a CombineMode
StrEnum + match statement. The dropdown choices in INPUT_TYPES are now
derived from the same Literal alias used in the function signature, so the
UI and the type contract can't drift apart.
- Smoke tests for PromptCombinerNode (14 tests) covering enum mapping, all
three modes, edge cases, and the unknown-mode error path. Brings combiner
coverage from 28% to 96%.
- Tidy preexisting issues surfaced by the new lint rules: B904 except chaining
in style_presets, RUF013 implicit Optional, RUF059 unused unpack, SIM117
nested-with consolidation in tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Remove dead imports from prompt_generator_node.py and ollama_client.py
- Switch optional imports to importlib.util.find_spec pattern
- Remove orphaned _cached_models/_cache_time from PromptGeneratorNode
- Remove unused seed param from PromptRefinerNode
- Add top_p input to NegativePromptNode with proper wiring
- Fix pytest.ini to not omit adapters from coverage
- Remove unused pytest imports from test files
- Run ruff check + format (all clean now)
- All 29 tests passing