VRS Native Image Recall → Generation State
Your own images as a tag suggestion engine
- encoder
- memory
- image
- vrs_state
- candidate_tags
- report
You have a folder of images with tags you already wrote. You have a new reference picture. This node answers the useful question - which of my old tags does this new thing look like? - and then packages the answer as context for a prompt expander.
What it is
VRS Native Image Recall → Generation State is the query half of the pack's VRS subsystem. You give it a vision encoder, a personal memory, one image and your creative request; it finds nearest neighbors in your own store, tallies the tags those neighbors carried, and builds a "generation state" - a bounded, structured packet of candidate tags and selected names.
Be clear on what it isn't. It's read-only retrieval, not training, not identification. The node's own description says candidate names are not automatic identity claims, and that's the single most important sentence on this page: an author or character name showing up in the candidate list is a neighbor vote, not a verified label. It also doesn't write prompts - that's the expander downstream.
Where it fits: tag-based models live and die on vocabulary, and the Danbooru tag space is enormous and specific. Having retrieval over your images means the tags that come back are ones you've actually used, in the phrasing your base model was trained on, instead of the model's guess at approximation. The pack's own honesty applies here too - small-store comparisons by the author showed neighbor appearance leaking into the subject and nonstandard tags surviving, so treat output as material to edit, not an oracle.
How it works
The image goes through the Gemma 4 E2B vision tower only - the same encoder the store was built with, verified by contract, and the node refuses with Incompatible VRS encoder if you mismatch (that message also fires for old legacy PIL/NF4 stores, which cannot be mixed with native ones).
The search is an exact full scan, not an ANN index. Vectors are held in a persistent disk-mapped FP32 pool, walked in bounded GPU blocks, so memory use stays predictable while the comparison itself is brute force. First query against a new snapshot is slow because it verifies the store and prepares that array; later queries just read the top neighbors' voting metadata.
Then it tallies. Tags get aggregated from the winning neighbors, excluding any entry whose decoded RGB pixel hash matches your query - so the image you're searching with doesn't vote for itself. That covers exact duplicates and re-saves, not crops or resized near-duplicates.
Finally it serializes a bounded generation state containing your explicit keep/target/exclude controls plus the candidate evidence. Only the names you explicitly select travel to the expander; everything else stays in the report as audit trail.
The inputs
Four are connections, six are yours to set.
encoder,memory,image,prompt- encoder fromVRS Native Vision Loader, memory fromVRS Native Memory Loader(orVRS Folder Memory), one image, and your creative request.keep_tags- the names and elements you want preserved, comma-separated. This is where you promote candidates: I looked at the suggestions and yes, this face is that character. The tooltip is literal - explicit names/elements to preserve.target_tags- the conditions you want changed.exclude_tags- what must not appear. A tag inexcludecannot also be inkeeportarget; the node raises rather than quietly picking a side.ratings- allowed local-source ratings, defaultg,s,q,e,u.uis unrated content, which is what your own generated images get. Dropuand your ComfyUI output disappears from retrieval results.neighbors- how many retrieved cases to vote from, default 32, up to 256.limit- how many candidate tags to keep, default 64, capped at 100.
Outputs: vrs_state (VRS_STATE, goes into the expander's vrs_state input - that's the whole point), candidate_tags (a comma-separated string, handy for eyeballing or for a text preview), and report (JSON with the full recommendation, the query pixel hash, encoder contract hash and weight size).
You can use candidate_tags without ever connecting a Gemma language model, which is a good way to test whether your memory is any good before paying for inference.
The two errors everybody hits
Enter the same creative request here and in Gemma Prompt Expander. The state is bound to the request it was built for. Paste the same text in prompt and in the expander's prompt - genuinely the same, including spacing-level fidelity - or the expander rejects the state with "request differs from the current prompt". The reasoning is sound: the reference material is conditioned on a specific ask, so it can't be reused for a different one.
VRS state and Gemma IMAGE differ; connect the same image to both nodes. If your state carries a native query, the expander verifies that the image you wired into it has the same decoded pixel hash as the one you recalled with. Connect the same Load Image output to both, or to neither.
Also: the image input takes exactly one RGB image (batch 1, H, W, 3). Select a single item before wiring a batch - this is one of the few places where a batch silently breaks a node's assumptions.
Install and setup order
cd ComfyUI/custom_nodes
git clone https://github.com/raspie10032/RS-Seasonal-Prompt-Generator
Restart when the queue is empty. The native VRS path needs no pip packages - ComfyUI's PyTorch and model management do the work, with no llama.cpp, Transformers, bitsandbytes or external retrieval server. It does need CUDA (no CPU fallback) and a native unquantized Gemma 4 E2B safetensors in models/text_encoders/; only the ~319 MiB of vision weights get loaded.
Order matters: build a memory first (VRS Native Build from JSONL pairs, or the VRS Folders panel for folders), then recall against it. Retrieval against an empty or unpublished store can't work no matter how the node is wired.
What I'd actually do with it
Set ratings to include u, keep neighbors around 32 and limit around 64 while you're learning what your store knows, put your intent in prompt, and just read candidate_tags for the first few runs. It's a surprisingly good way to discover you've been inventing tag spellings. Once the candidates look sane, promote the two or three you care about into keep_tags and let the expander do the writing.
Inputs (10)
| Name | Type | Default | Description |
|---|---|---|---|
| encoder | VRS_ENCODER | — | |
| memory | VRS_MEMORY | — | |
| image | IMAGE | — | |
| prompt | STRING | — | |
| keep_tags | STRING | Explicit names/elements to preserve, comma-separated. | |
| target_tags | STRING | — | |
| exclude_tags | STRING | — | |
| ratings | STRING | g,s,q,e,u | Allowed local-source ratings; u is unrated generated artwork. |
| neighbors | INT | 321–256 | — |
| limit | INT | 641–100 | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| vrs_state | VRS_STATE | — |
| candidate_tags | STRING | — |
| report | STRING | — |