Files
jeremie louvaertandClaude Opus 4.7 6d399c8f52 Add Hash Vault sample workflow, v1.1.0
workflows/hash_vault_basic.json wires the canonical cache-skip pattern:
one StringConstantMultiline feeds both DeterministicHashVault (for hashing)
and GeminiImageGenerate (for the actual API call). On cache hit the Lazy
API Switch short-circuits the Gemini branch entirely — no API call, no
charge. On miss, Gemini runs, HashVaultSave writes the result to disk,
and the next run with the same prompt is a hit.

README updated with a walkthrough and the pack dependencies
(ComfyUI-API-Optimizer, ComfyUI-Gemini-Direct, ComfyUI-KJNodes for the
StringConstantMultiline primitive — any STRING-output node works in
its place).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:42:57 +02:00

513 lines
11 KiB
JSON

{
"id": "84148115-6967-430a-917d-875f36034cab",
"revision": 0,
"last_node_id": 7,
"last_link_id": 8,
"nodes": [
{
"id": 1,
"type": "Note",
"pos": [
40,
20
],
"size": [
1560,
180
],
"flags": {},
"order": 0,
"mode": 0,
"inputs": [],
"outputs": [],
"properties": {},
"widgets_values": [
"HASH VAULT - skip the API call when you've already generated this exact prompt.\n\nFLOW\nOne prompt feeds BOTH Hash Vault (which computes a SHA-256 of it) AND Gemini. Hash Vault looks in output/hash_vault/ for a .pt file matching that hash. If found -> cache hit, is_cached=1, cached_data contains the image. If not -> cache miss, is_cached=0.\n\nLazy API Switch reads is_cached and demands ONLY ONE branch:\n hit -> reads cached_data. The Gemini -> Hash Vault Save branch is never executed. $0.00 billed.\n miss -> demands api_data, which forces Gemini to run, then Hash Vault Save writes the result to disk so the next run with the same prompt is a hit.\n\nCHANGE THE PROMPT -> new hash -> cache miss -> Gemini runs. Re-run with the same prompt -> cache hit -> no API call, no cost.\n\nREQUIRES\nComfyUI-API-Optimizer (Hash Vault nodes), ComfyUI-Gemini-Direct (Gemini node), ComfyUI-KJNodes (StringConstantMultiline). Any STRING primitive works in place of KJNodes' node - the point is that a single string source drives both the hash and the API input."
]
},
{
"id": 2,
"type": "StringConstantMultiline",
"pos": [
40,
240
],
"size": [
360,
200
],
"flags": {},
"order": 1,
"mode": 0,
"inputs": [
{
"localized_name": "string",
"name": "string",
"type": "STRING",
"widget": {
"name": "string"
},
"link": null
},
{
"localized_name": "strip_newlines",
"name": "strip_newlines",
"type": "BOOLEAN",
"widget": {
"name": "strip_newlines"
},
"link": null
}
],
"outputs": [
{
"localized_name": "STRING",
"name": "STRING",
"type": "STRING",
"links": [
1,
2
],
"slot_index": 0
}
],
"properties": {
"cnr_id": "comfyui-kjnodes",
"ver": "1.1.6",
"Node name for S&R": "StringConstantMultiline",
"aux_id": "kijai/ComfyUI-KJNodes"
},
"widgets_values": [
"A cinematic portrait of an elderly Parisian bookseller in his shop, golden hour side light through the window, leather-bound books out of focus behind him, shot on Leica M with a 50mm Summilux, f/1.4, subtle film grain, warm neutral palette",
false
]
},
{
"id": 3,
"type": "DeterministicHashVault",
"pos": [
440,
240
],
"size": [
340,
140
],
"flags": {},
"order": 2,
"mode": 0,
"inputs": [
{
"localized_name": "payload_string",
"name": "payload_string",
"type": "STRING",
"link": 1
},
{
"localized_name": "any_input",
"name": "any_input",
"type": "*",
"shape": 7,
"link": null
},
{
"localized_name": "cache_ttl_hours",
"name": "cache_ttl_hours",
"type": "FLOAT",
"widget": {
"name": "cache_ttl_hours"
},
"shape": 7,
"link": null
}
],
"outputs": [
{
"localized_name": "cached_data",
"name": "cached_data",
"type": "*",
"links": [
3
],
"slot_index": 0
},
{
"localized_name": "is_cached",
"name": "is_cached",
"type": "INT",
"links": [
4
],
"slot_index": 1
},
{
"localized_name": "hash_key",
"name": "hash_key",
"type": "STRING",
"links": [
5
],
"slot_index": 2
}
],
"properties": {
"cnr_id": "comfyui-api-optimizer",
"ver": "1.0.0",
"Node name for S&R": "DeterministicHashVault",
"aux_id": "jeremieLouvaert/ComfyUI-API-Optimizer"
},
"widgets_values": [
0.0
]
},
{
"id": 4,
"type": "GeminiImageGenerate",
"pos": [
440,
520
],
"size": [
380,
320
],
"flags": {},
"order": 3,
"mode": 0,
"inputs": [
{
"localized_name": "prompt",
"name": "prompt",
"type": "STRING",
"widget": {
"name": "prompt"
},
"link": 2
},
{
"localized_name": "model",
"name": "model",
"type": "COMBO",
"widget": {
"name": "model"
},
"link": null
},
{
"localized_name": "seed",
"name": "seed",
"type": "INT",
"widget": {
"name": "seed"
},
"link": null
},
{
"localized_name": "aspect_ratio",
"name": "aspect_ratio",
"type": "COMBO",
"widget": {
"name": "aspect_ratio"
},
"link": null
},
{
"localized_name": "resolution",
"name": "resolution",
"type": "COMBO",
"widget": {
"name": "resolution"
},
"link": null
},
{
"localized_name": "response_modalities",
"name": "response_modalities",
"type": "COMBO",
"widget": {
"name": "response_modalities"
},
"link": null
},
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"shape": 7,
"link": null
},
{
"localized_name": "system_prompt",
"name": "system_prompt",
"type": "STRING",
"widget": {
"name": "system_prompt"
},
"shape": 7,
"link": null
},
{
"localized_name": "api_key",
"name": "api_key",
"type": "STRING",
"widget": {
"name": "api_key"
},
"shape": 7,
"link": null
}
],
"outputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"links": [
6
],
"slot_index": 0
},
{
"localized_name": "text",
"name": "text",
"type": "STRING",
"links": [],
"slot_index": 1
},
{
"localized_name": "cost_info",
"name": "cost_info",
"type": "STRING",
"links": [],
"slot_index": 2
},
{
"localized_name": "cache_key",
"name": "cache_key",
"type": "STRING",
"links": [],
"slot_index": 3
}
],
"properties": {
"cnr_id": "comfyui-gemini-direct",
"ver": "1.0.0",
"Node name for S&R": "GeminiImageGenerate",
"aux_id": "jeremieLouvaert/ComfyUI-Gemini-Direct"
},
"widgets_values": [
"",
"gemini-3-pro-image-preview",
0,
"2:3",
"2K",
"IMAGE+TEXT",
"",
""
]
},
{
"id": 5,
"type": "HashVaultSave",
"pos": [
860,
520
],
"size": [
300,
80
],
"flags": {},
"order": 4,
"mode": 0,
"inputs": [
{
"localized_name": "hash_key",
"name": "hash_key",
"type": "STRING",
"link": 5
},
{
"localized_name": "api_output",
"name": "api_output",
"type": "*",
"link": 6
}
],
"outputs": [
{
"localized_name": "api_output",
"name": "api_output",
"type": "*",
"links": [
7
],
"slot_index": 0
}
],
"properties": {
"cnr_id": "comfyui-api-optimizer",
"ver": "1.0.0",
"Node name for S&R": "HashVaultSave",
"aux_id": "jeremieLouvaert/ComfyUI-API-Optimizer"
},
"widgets_values": []
},
{
"id": 6,
"type": "LazyAPISwitch",
"pos": [
1200,
240
],
"size": [
300,
120
],
"flags": {},
"order": 5,
"mode": 0,
"inputs": [
{
"localized_name": "is_cached",
"name": "is_cached",
"type": "INT",
"link": 4
},
{
"localized_name": "cached_data",
"name": "cached_data",
"type": "*",
"shape": 7,
"link": 3
},
{
"localized_name": "api_data",
"name": "api_data",
"type": "*",
"shape": 7,
"link": 7
}
],
"outputs": [
{
"localized_name": "final_output",
"name": "final_output",
"type": "*",
"links": [
8
],
"slot_index": 0
}
],
"properties": {
"cnr_id": "comfyui-api-optimizer",
"ver": "1.0.0",
"Node name for S&R": "LazyAPISwitch",
"aux_id": "jeremieLouvaert/ComfyUI-API-Optimizer"
},
"widgets_values": []
},
{
"id": 7,
"type": "PreviewImage",
"pos": [
1540,
240
],
"size": [
420,
400
],
"flags": {},
"order": 6,
"mode": 0,
"inputs": [
{
"localized_name": "images",
"name": "images",
"type": "IMAGE",
"link": 8
}
],
"outputs": [],
"properties": {
"cnr_id": "comfy-core",
"ver": "0.18.1",
"Node name for S&R": "PreviewImage"
},
"widgets_values": []
}
],
"links": [
[
1,
2,
0,
3,
0,
"STRING"
],
[
2,
2,
0,
4,
0,
"STRING"
],
[
3,
3,
0,
6,
1,
"*"
],
[
4,
3,
1,
6,
0,
"INT"
],
[
5,
3,
2,
5,
0,
"STRING"
],
[
6,
4,
0,
5,
1,
"IMAGE"
],
[
7,
5,
0,
6,
2,
"*"
],
[
8,
6,
0,
7,
0,
"IMAGE"
]
],
"groups": [],
"config": {},
"extra": {},
"version": 0.4
}