Full README/docs/index.md rewrite from a product/UX-first structure
(what-is-this, why, then a deep-dive per node group with screenshots)
instead of an incremental changelog-style read.
All screenshots regenerated from a real, running ComfyUI instance —
not reused. This surfaced and fixed two more bugs in
scripts/take_screenshots.py, the project's own screenshot-automation
tool, which nobody had run since before the ADR-007 rename (the old
committed screenshots still showed pre-rename node names, e.g.
"Ollama Chat Completion" instead of "Chat Completion"):
- Every LiteGraph.createNode() call used the pre-rename node type
strings (OllamaChatCompletion/OllamaLoadModel/OllamaUnloadModel);
fixed to the current names.
- The canvas locator ("canvas#graph-canvas, canvas") silently
grabbed a 250x200 minimap canvas ComfyUI's frontend added since
this script was last verified, instead of the real graph — every
screenshot failed with a clipping error. Fixed to target
#graph-canvas explicitly.
Two new scenes added, addressing the actual gap: existing
screenshots showed connectivity and options, never the two things
this whole epic sequence was about — structured output (the live
dynamic output sockets appearing as output_schema is edited, via the
real js/ollama.js widget callbacks the previous commit fixed) and
llama.cpp (LlamaCppClient wired into the same generic ChatCompletion
node Ollama uses — the actual point of the adapter pattern).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132ojafeazQ3ephcBejEWFj
Second LLMProvider (ADR-007), backed by llama-server's router mode. Only
one new ComfyUI node — LlamaCppClient, emitting the same LLM_CLIENT socket
Ollama Client does. ChatCompletion, LLMModelSelector, LLMLoadModel,
LLMUnloadModel work unmodified once wired to it — the actual proof the
provider abstraction generalizes, not just Ollama-shaped in practice.
API shape verified live against ggml-org/llama.cpp's tools/server/README.md
(research.md) rather than assumed: model identifier field is "id" (not
"name"), status is a nested {"value": "..."} object. list_models() needs
no normalization — llama.cpp's status vocabulary is exactly ModelStatus's
full set, unlike Ollama's narrower one.
38 new tests (test_llamacpp_provider.py, test_llamacpp.py), including a
dedicated US4 test proving no generic node branches on provider type —
the same call sequence succeeds against an Ollama-shaped or llama.cpp-
shaped fake provider.
README/docs updated for the new node this time, not left stale.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132ojafeazQ3ephcBejEWFj
README.md and docs/index.md still named OllamaChatCompletion/OllamaModelSelector/
OllamaLoadModel/OllamaUnloadModel and the OLLAMA_CLIENT socket — stale after
the atomic cutover. Updated both (they're content-duplicates) and added the
old->new migration table FR-009 requires, which previously only existed as
a code comment (comfydv.ollama.MIGRATION_MAP), not surfaced to users.
Found via a dedicated docs-freshness pass, not caught by the beacon-reviewer
correctness review (which is scoped to code, not prose).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0132ojafeazQ3ephcBejEWFj
- README.md + docs/index.md: fix stale wiring instructions that referenced
model_name optional input (removed); now correctly shows wiring
OllamaLoadModel.model_name → OllamaChatCompletion.model (STRING)
- ChatCompletion description updated to mention inline response display
- Minimal chat workflow prose updated to reflect STRING model input + inline
display (no separate Text Display node needed)
- .github/workflows/docs.yml: auto-deploy docs via mike on push to main;
reads version from pyproject.toml, deploys as <version> + stable alias
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
- OllamaChatCompletion.model changed from COMBO to plain STRING so it can
receive a wired value from OllamaLoadModel without a separate model_name
socket (removes dual-input UX confusion and COMBO validation errors)
- Removed model_name optional input — model STRING is the single entry point
- _load_default_models() tries OLLAMA_HOST env var → host.docker.internal →
localhost so COMBO nodes pre-populate on server start inside Docker
- docker-compose.dev.yml: OLLAMA_HOST=http://host.docker.internal:11434 so
model dropdowns are populated at startup without a manual Refresh click
- JS: refreshModelWidget handles both COMBO and STRING widget types; keeps
user-typed/wired value when it is already a known model
- Tests: first_generative_model fixture dynamically selects the first
installed model instead of hardcoding embeddinggemma:latest (which is an
embedding-only model that rejects /api/generate with HTTP 400)
- Tests: test_multi_turn_receives_context passes think=False to prevent
Qwen3 thinking-mode models from returning empty content fields
- Screenshots retaken: ollama_chat.png, ollama_lifecycle.png, ollama_workflow.png
All 80 unit + 9 integration tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
OllamaChatCompletion grew taller with the new timeout_secs widget.
Nodes repositioned with generous horizontal gaps (60→380→720→1200)
and scale reduced to 0.72 so the full Load→Chat→Unload chain is
visible without overlap.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
OllamaUnloadModel now works as a pass-through node: wire
OllamaChatCompletion.response into the optional `passthrough` input and
OllamaChatCompletion.model_name into `model` — ComfyUI's data-flow graph
then guarantees Load → Chat → Unload execution order.
OllamaChatCompletion gains an optional `model_name` forceInput so
OllamaLoadModel.model_name can wire in (creating Load → Chat ordering) and
overrides the COMBO dropdown. It also returns `model_name` as a 3rd output
so the model can flow downstream to UnloadModel without duplication.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
Fresh Playwright sessions landed on ComfyUI's template gallery (no `workflow`
key in localStorage) instead of the node canvas. Pre-seeding an empty workflow
via Playwright storage_state fixes the capture. Also shows localhost:11434 in
the OllamaClient screenshot (user-facing default) rather than the Docker-internal
routing URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
Mirrors the README.md rewrite into the MkDocs site landing page:
- Add Circuit Breaker section with screenshot (assets/circuit_breaker.png)
- Update Format String and Random Choice prose to match README
- Replace stale dependency list (rich/colorama/termcolor removed in spec 001)
- Add ComfyUI Manager install path
- Image paths use assets/ (MkDocs-relative) vs docs/assets/ in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
Replace hand-crafted PowerPoint exports with Playwright-generated screenshots
taken from the actual running Docker test harness:
- scripts/take_screenshots.py: headless Chromium via Playwright; creates nodes
programmatically via LiteGraph/window.app, triggers updateNodeConfig() for
FormatString's dynamic socket update, then crops tightly around each node
- docs/assets/: four screenshots (fstring, jinja2, random, circuit_breaker)
replacing the old 1920×1080 PowerPoint exports (90%+ smaller, more accurate)
- Justfile: add `screenshots` recipe (starts harness if needed, waits healthy,
runs the script)
- README.md: rewrite to cover all three nodes including Circuit Breaker;
fix stale dependency list (was listing rich/colorama/termcolor which we
removed in spec 001); add ComfyUI Manager install path
- pyproject.toml + uv.lock: add playwright to [dev] dependencies
Run: just screenshots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RWysE6jYn4YjNorLBQ1cXb
BREAKING CHANGE: Output order changed to put primary outputs first
- formatted_string now always in output position 0 (was dynamic)
- saved_file_path now always in output position 1 (was dynamic)
- Variable pass-through outputs now in positions 2+ (were 0+)
This change ensures primary outputs are in fixed, predictable positions,
resolving ComfyUI output mapping issues when dynamic outputs are present.
Changes:
- Updated format_string() return statement to reverse output order
- Updated update_widget() to set RETURN_TYPES/RETURN_NAMES in new order
- Updated all 47 tests to expect new output positions
- Updated all docstrings and examples in format_string.py
- Added comprehensive "Output Structure" documentation to README.md
- Added same documentation to docs/index.md for consistency
- All tests passing ✅