Skip to content

Commit fc6196d

Browse files
committed
comfyui nodes
1 parent 29138e9 commit fc6196d

4 files changed

Lines changed: 246 additions & 0 deletions

File tree

my-apps/ai/comfyui/configmap.yaml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,11 @@ data:
7272
# ── System Setup ───────────────────────────────────────────
7373
mkdir -p /usr/share/fonts/truetype
7474
75+
# ── Bridge Nodes (from ConfigMap) ─────────────────────────
76+
echo "[INFO] Installing bridge nodes..."
77+
cp /opt/custom-nodes/image_to_llamacpp_base64.py \
78+
/root/ComfyUI/custom_nodes/image_to_llamacpp_base64.py
79+
7580
# ── Example Workflows ──────────────────────────────────────
7681
DEST="/root/ComfyUI/user/default/workflows"
7782
mkdir -p "$DEST"
@@ -82,3 +87,74 @@ data:
8287
fi
8388
8489
echo "[INFO] Pre-start setup complete."
90+
---
91+
apiVersion: v1
92+
kind: ConfigMap
93+
metadata:
94+
name: comfyui-custom-nodes
95+
namespace: comfyui
96+
data:
97+
image_to_llamacpp_base64.py: |
98+
"""Bridge node: converts ComfyUI IMAGE tensor to base64 JSON for LlamaCppClient."""
99+
100+
import base64
101+
import io
102+
import json
103+
104+
import numpy as np
105+
from PIL import Image
106+
107+
108+
class ImageToLlamaCppBase64:
109+
@classmethod
110+
def INPUT_TYPES(cls):
111+
return {
112+
"required": {
113+
"image": ("IMAGE",),
114+
},
115+
"optional": {
116+
"prompt": (
117+
"STRING",
118+
{
119+
"default": "Describe this image in rich detail for use as an image generation prompt. "
120+
"Focus on subject, composition, lighting, colors, style, and mood. "
121+
"Output only the prompt text, no preamble.",
122+
"multiline": True,
123+
},
124+
),
125+
},
126+
}
127+
128+
RETURN_TYPES = ("STRING", "STRING")
129+
RETURN_NAMES = ("image_data", "user_message")
130+
FUNCTION = "convert"
131+
CATEGORY = "AI/LlamaCpp"
132+
133+
def convert(
134+
self,
135+
image,
136+
prompt="Describe this image in rich detail for use as an image generation prompt. "
137+
"Focus on subject, composition, lighting, colors, style, and mood. "
138+
"Output only the prompt text, no preamble.",
139+
):
140+
# IMAGE tensor shape: [batch, height, width, channels] float32 0-1
141+
img_array = (image[0].cpu().numpy() * 255).astype(np.uint8)
142+
pil_image = Image.fromarray(img_array)
143+
144+
buffer = io.BytesIO()
145+
pil_image.save(buffer, format="PNG")
146+
b64_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
147+
148+
image_data = json.dumps([{"data": f"data:image/png;base64,{b64_str}", "id": 1}])
149+
user_message = f"[img-1] {prompt}"
150+
151+
return (image_data, user_message)
152+
153+
154+
NODE_CLASS_MAPPINGS = {
155+
"ImageToLlamaCppBase64": ImageToLlamaCppBase64,
156+
}
157+
158+
NODE_DISPLAY_NAME_MAPPINGS = {
159+
"ImageToLlamaCppBase64": "Image to LlamaCpp Base64",
160+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Bridge node: converts ComfyUI IMAGE tensor to base64 JSON for LlamaCppClient."""
2+
3+
import base64
4+
import io
5+
import json
6+
7+
import numpy as np
8+
from PIL import Image
9+
10+
11+
class ImageToLlamaCppBase64:
12+
@classmethod
13+
def INPUT_TYPES(cls):
14+
return {
15+
"required": {
16+
"image": ("IMAGE",),
17+
},
18+
"optional": {
19+
"prompt": (
20+
"STRING",
21+
{
22+
"default": "Describe this image in rich detail for use as an image generation prompt. "
23+
"Focus on subject, composition, lighting, colors, style, and mood. "
24+
"Output only the prompt text, no preamble.",
25+
"multiline": True,
26+
},
27+
),
28+
},
29+
}
30+
31+
RETURN_TYPES = ("STRING", "STRING")
32+
RETURN_NAMES = ("image_data", "user_message")
33+
FUNCTION = "convert"
34+
CATEGORY = "AI/LlamaCpp"
35+
36+
def convert(
37+
self,
38+
image,
39+
prompt="Describe this image in rich detail for use as an image generation prompt. "
40+
"Focus on subject, composition, lighting, colors, style, and mood. "
41+
"Output only the prompt text, no preamble.",
42+
):
43+
# IMAGE tensor shape: [batch, height, width, channels] float32 0-1
44+
img_array = (image[0].cpu().numpy() * 255).astype(np.uint8)
45+
pil_image = Image.fromarray(img_array)
46+
47+
buffer = io.BytesIO()
48+
pil_image.save(buffer, format="PNG")
49+
b64_str = base64.b64encode(buffer.getvalue()).decode("utf-8")
50+
51+
image_data = json.dumps([{"data": f"data:image/png;base64,{b64_str}", "id": 1}])
52+
user_message = f"[img-1] {prompt}"
53+
54+
return (image_data, user_message)
55+
56+
57+
NODE_CLASS_MAPPINGS = {
58+
"ImageToLlamaCppBase64": ImageToLlamaCppBase64,
59+
}
60+
61+
NODE_DISPLAY_NAME_MAPPINGS = {
62+
"ImageToLlamaCppBase64": "Image to LlamaCpp Base64",
63+
}

my-apps/ai/comfyui/deployment.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ spec:
7777
- name: manager-config
7878
mountPath: /root/ComfyUI/user/__manager/config.ini
7979
subPath: config.ini
80+
- name: custom-nodes
81+
mountPath: /opt/custom-nodes
82+
readOnly: true
8083
readinessProbe:
8184
httpGet:
8285
path: /
@@ -104,3 +107,6 @@ spec:
104107
configMap:
105108
name: comfyui-pre-start
106109
defaultMode: 0755
110+
- name: custom-nodes
111+
configMap:
112+
name: comfyui-custom-nodes
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
{
2+
"1": {
3+
"class_type": "LoadImage",
4+
"inputs": {
5+
"image": "example.png"
6+
}
7+
},
8+
"2": {
9+
"class_type": "ImageToLlamaCppBase64",
10+
"inputs": {
11+
"image": ["1", 0],
12+
"prompt": "Describe this image in rich detail for use as an image generation prompt. Focus on subject, composition, lighting, colors, style, and mood. Output only the prompt text, no preamble."
13+
}
14+
},
15+
"3": {
16+
"class_type": "LlamaCppClient",
17+
"inputs": {
18+
"server_url": "http://llama-cpp-service.llama-cpp.svc.cluster.local:8080",
19+
"endpoint": "chat_completions",
20+
"prompt": "",
21+
"system_message": "You are an expert image description assistant. When given an image, produce a detailed, vivid text-to-image prompt that would recreate a similar image. Include subject, setting, lighting, colors, composition, artistic style, and mood. Output ONLY the prompt text with no preamble, labels, or explanation.",
22+
"user_message": ["2", 1],
23+
"image_data": ["2", 0],
24+
"temperature": 0.6,
25+
"n_predict": 256,
26+
"max_tokens": 256
27+
}
28+
},
29+
"4": {
30+
"class_type": "CLIPTextEncode",
31+
"inputs": {
32+
"text": ["3", 0],
33+
"clip": ["6", 0]
34+
}
35+
},
36+
"5": {
37+
"class_type": "CLIPTextEncode",
38+
"inputs": {
39+
"text": "blurry, low quality, distorted, watermark, text, deformed",
40+
"clip": ["6", 0]
41+
}
42+
},
43+
"6": {
44+
"class_type": "DualCLIPLoader",
45+
"inputs": {
46+
"clip_name1": "clip_l.safetensors",
47+
"clip_name2": "t5xxl_fp8_e4m3fn.safetensors",
48+
"type": "flux"
49+
}
50+
},
51+
"7": {
52+
"class_type": "UNETLoader",
53+
"inputs": {
54+
"unet_name": "z_image_turbo_bf16.safetensors",
55+
"weight_dtype": "fp8_e4m3fn"
56+
}
57+
},
58+
"8": {
59+
"class_type": "VAELoader",
60+
"inputs": {
61+
"vae_name": "ae.safetensors"
62+
}
63+
},
64+
"9": {
65+
"class_type": "EmptyLatentImage",
66+
"inputs": {
67+
"width": 1024,
68+
"height": 1024,
69+
"batch_size": 1
70+
}
71+
},
72+
"10": {
73+
"class_type": "KSampler",
74+
"inputs": {
75+
"seed": 0,
76+
"steps": 9,
77+
"cfg": 1.0,
78+
"sampler_name": "euler",
79+
"scheduler": "normal",
80+
"denoise": 1.0,
81+
"model": ["7", 0],
82+
"positive": ["4", 0],
83+
"negative": ["5", 0],
84+
"latent_image": ["9", 0]
85+
}
86+
},
87+
"11": {
88+
"class_type": "VAEDecode",
89+
"inputs": {
90+
"samples": ["10", 0],
91+
"vae": ["8", 0]
92+
}
93+
},
94+
"12": {
95+
"class_type": "SaveImage",
96+
"inputs": {
97+
"filename_prefix": "vision-to-image",
98+
"images": ["11", 0]
99+
}
100+
}
101+
}

0 commit comments

Comments
 (0)