Instructions to use LuckyOda/comfyui-carbonara-bundle with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- llama-cpp-python
How to use LuckyOda/comfyui-carbonara-bundle with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="LuckyOda/comfyui-carbonara-bundle", filename="models/text_encoders/qwen-4b-zimage-heretic-q8.gguf", )
llm.create_chat_completion( messages = "No input example has been defined for this model task." )
- Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use LuckyOda/comfyui-carbonara-bundle with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf LuckyOda/comfyui-carbonara-bundle # Run inference directly in the terminal: llama cli -hf LuckyOda/comfyui-carbonara-bundle
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf LuckyOda/comfyui-carbonara-bundle # Run inference directly in the terminal: llama cli -hf LuckyOda/comfyui-carbonara-bundle
Use pre-built binary
# Download pre-built binary from: # https://github.com/ggerganov/llama.cpp/releases # Start a local OpenAI-compatible server with a web UI: ./llama-server -hf LuckyOda/comfyui-carbonara-bundle # Run inference directly in the terminal: ./llama-cli -hf LuckyOda/comfyui-carbonara-bundle
Build from source code
git clone https://github.com/ggerganov/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-server llama-cli # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama-server -hf LuckyOda/comfyui-carbonara-bundle # Run inference directly in the terminal: ./build/bin/llama-cli -hf LuckyOda/comfyui-carbonara-bundle
Use Docker
docker model run hf.co/LuckyOda/comfyui-carbonara-bundle
- LM Studio
- Jan
- Ollama
How to use LuckyOda/comfyui-carbonara-bundle with Ollama:
ollama run hf.co/LuckyOda/comfyui-carbonara-bundle
- Unsloth Studio
How to use LuckyOda/comfyui-carbonara-bundle with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for LuckyOda/comfyui-carbonara-bundle to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for LuckyOda/comfyui-carbonara-bundle to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for LuckyOda/comfyui-carbonara-bundle to start chatting
- Pi
How to use LuckyOda/comfyui-carbonara-bundle with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf LuckyOda/comfyui-carbonara-bundle
Configure the model in Pi
# Install Pi: npm install -g @mariozechner/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "LuckyOda/comfyui-carbonara-bundle" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent new
How to use LuckyOda/comfyui-carbonara-bundle with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf LuckyOda/comfyui-carbonara-bundle
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default LuckyOda/comfyui-carbonara-bundle
Run Hermes
hermes
- Atomic Chat new
- OpenClaw new
How to use LuckyOda/comfyui-carbonara-bundle with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf LuckyOda/comfyui-carbonara-bundle
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "LuckyOda/comfyui-carbonara-bundle" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
- Docker Model Runner
How to use LuckyOda/comfyui-carbonara-bundle with Docker Model Runner:
docker model run hf.co/LuckyOda/comfyui-carbonara-bundle
- Lemonade
How to use LuckyOda/comfyui-carbonara-bundle with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull LuckyOda/comfyui-carbonara-bundle
Run and chat with the model
lemonade run user.comfyui-carbonara-bundle-{{QUANT_TAG}}List all available models
lemonade list
| // SPDX-License-Identifier: MIT | |
| // Copyright (C) 2025 ComfyUI-Multiband Contributors | |
| /** | |
| * Preview Multiband Image - Dynamic channel switching without re-execution. | |
| * All channels are pre-rendered for all batch images; JS switches between them instantly. | |
| */ | |
| import { app } from "../../../scripts/app.js"; | |
| app.registerExtension({ | |
| name: "multiband.PreviewMultibandImage", | |
| async beforeRegisterNodeDef(nodeType, nodeData, app) { | |
| if (nodeData.name !== "MultibandPreview") return; | |
| const onNodeCreated = nodeType.prototype.onNodeCreated; | |
| nodeType.prototype.onNodeCreated = function() { | |
| const r = onNodeCreated?.apply(this, arguments); | |
| const node = this; | |
| this._channelNames = []; | |
| this._channelStats = []; // [{global: [min,max], per_sample: [[min,max],...]}, ...] | |
| this._allChannelImages = []; // [channel][batch] structure | |
| this._currentChannel = 0; | |
| this._batchSize = 1; | |
| this._selectedImageIndex = -1; // -1 = all images, >=0 = specific image | |
| // Setup channel widget with dynamic switching | |
| const setupChannelWidget = () => { | |
| const channelWidget = node.widgets?.find(w => w.name === "channel_index"); | |
| if (!channelWidget || !node.widgets) return; | |
| // Move widget to end (below images) | |
| const idx = node.widgets.indexOf(channelWidget); | |
| if (idx > -1) { | |
| node.widgets.splice(idx, 1); | |
| node.widgets.push(channelWidget); | |
| } | |
| // Store reference | |
| node._channelWidget = channelWidget; | |
| // Override the widget's callback to switch images dynamically | |
| const originalCallback = channelWidget.callback; | |
| channelWidget.callback = function(value) { | |
| if (originalCallback) originalCallback.call(this, value); | |
| // Switch displayed images without re-running node | |
| if (node._allChannelImages && node._allChannelImages.length > 0) { | |
| const channelIdx = Math.min(value, node._allChannelImages.length - 1); | |
| const channelBatchImages = node._allChannelImages[channelIdx]; | |
| if (channelBatchImages && channelBatchImages.length > 0) { | |
| // Load ALL batch images for this channel | |
| const loadPromises = channelBatchImages.map((imgInfo, batchIdx) => { | |
| return new Promise((resolve) => { | |
| const imgUrl = `/view?filename=${encodeURIComponent(imgInfo.filename)}&type=${imgInfo.type}&subfolder=${encodeURIComponent(imgInfo.subfolder || '')}`; | |
| const img = new Image(); | |
| img.onload = () => resolve({ img, batchIdx }); | |
| img.onerror = () => resolve(null); | |
| img.src = imgUrl; | |
| }); | |
| }); | |
| Promise.all(loadPromises).then((results) => { | |
| // Filter out failed loads and sort by batch index | |
| const loadedImages = results | |
| .filter(r => r !== null) | |
| .sort((a, b) => a.batchIdx - b.batchIdx) | |
| .map(r => r.img); | |
| if (loadedImages.length > 0) { | |
| node.imgs = loadedImages; | |
| node._currentChannel = channelIdx; | |
| node._selectedImageIndex = -1; // Reset selection on channel change | |
| node.setDirtyCanvas(true, true); | |
| } | |
| }); | |
| } | |
| } | |
| }; | |
| // Update max value based on available channels | |
| if (node._allChannelImages && node._allChannelImages.length > 0) { | |
| channelWidget.options = channelWidget.options || {}; | |
| channelWidget.options.max = node._allChannelImages.length - 1; | |
| } | |
| }; | |
| // Setup after widgets are created | |
| setTimeout(setupChannelWidget, 100); | |
| return r; | |
| }; | |
| // Handle execution results - store all channel images | |
| const onExecuted = nodeType.prototype.onExecuted; | |
| nodeType.prototype.onExecuted = function(message) { | |
| onExecuted?.apply(this, arguments); | |
| // Store all channel images for dynamic switching | |
| // Structure: [channel_idx][batch_idx] | |
| if (message?.all_channel_images && message.all_channel_images.length > 0) { | |
| this._allChannelImages = message.all_channel_images[0]; | |
| const numChannels = this._allChannelImages.length; | |
| const batchSize = this._allChannelImages[0]?.length || 0; | |
| console.log("[MultibandPreview] Loaded", numChannels, "channels x", batchSize, "batch images"); | |
| // Update widget max value | |
| const channelWidget = this._channelWidget; | |
| if (channelWidget) { | |
| channelWidget.options = channelWidget.options || {}; | |
| channelWidget.options.max = numChannels - 1; | |
| } | |
| } | |
| // Store batch size | |
| if (message?.batch_size && message.batch_size.length > 0) { | |
| this._batchSize = message.batch_size[0]; | |
| } | |
| // Store channel names | |
| if (message?.channel_names && message.channel_names.length > 0) { | |
| this._channelNames = message.channel_names[0]; | |
| console.log("[MultibandPreview] Channels:", this._channelNames); | |
| } | |
| // Store channel stats (min/max) | |
| if (message?.channel_stats && message.channel_stats.length > 0) { | |
| this._channelStats = message.channel_stats[0]; | |
| console.log("[MultibandPreview] Stats:", this._channelStats); | |
| } | |
| // Store current channel | |
| if (message?.current_channel && message.current_channel.length > 0) { | |
| this._currentChannel = message.current_channel[0]; | |
| } | |
| }; | |
| // Draw info bar using onDrawForeground - position at TOP below title | |
| const onDrawForeground = nodeType.prototype.onDrawForeground; | |
| nodeType.prototype.onDrawForeground = function(ctx) { | |
| if (onDrawForeground) onDrawForeground.apply(this, arguments); | |
| // Only draw if we have channel data | |
| if (!this._channelNames || this._channelNames.length === 0) return; | |
| if (!this._channelStats || this._channelStats.length === 0) return; | |
| const ch = this._currentChannel || 0; | |
| const channelName = this._channelNames[ch] || `channel_${ch}`; | |
| const stats = this._channelStats[ch]; | |
| if (!stats) return; | |
| // Determine if showing global or per-sample stats | |
| const selectedIdx = this._selectedImageIndex; | |
| let minVal, maxVal, rangeLabel; | |
| if (selectedIdx >= 0 && selectedIdx < (stats.per_sample?.length || 0)) { | |
| const ps = stats.per_sample[selectedIdx]; | |
| minVal = ps[0]; | |
| maxVal = ps[1]; | |
| rangeLabel = `sample ${selectedIdx}`; | |
| } else { | |
| minVal = stats.global[0]; | |
| maxVal = stats.global[1]; | |
| rangeLabel = "global"; | |
| } | |
| // Format numbers | |
| const fmt = (v) => { | |
| if (Math.abs(v) < 0.001 || Math.abs(v) >= 10000) { | |
| return v.toExponential(2); | |
| } | |
| return v.toFixed(3); | |
| }; | |
| const infoText = `ch${ch}: ${channelName} [${fmt(minVal)}, ${fmt(maxVal)}] (${rangeLabel})`; | |
| ctx.save(); | |
| // Draw at TOP of node, just below title (y=0 is top of node content area) | |
| const barHeight = 16; | |
| const y = 0; | |
| // Background bar - full width, semi-transparent dark | |
| ctx.fillStyle = "rgba(20, 30, 50, 0.9)"; | |
| ctx.fillRect(0, y, this.size[0], barHeight); | |
| // Bottom border line | |
| ctx.strokeStyle = "rgba(80, 120, 180, 0.6)"; | |
| ctx.lineWidth = 1; | |
| ctx.beginPath(); | |
| ctx.moveTo(0, y + barHeight); | |
| ctx.lineTo(this.size[0], y + barHeight); | |
| ctx.stroke(); | |
| // Text | |
| ctx.font = "10px monospace"; | |
| ctx.fillStyle = "#99ccff"; | |
| ctx.textAlign = "center"; | |
| ctx.textBaseline = "middle"; | |
| ctx.fillText(infoText, this.size[0] / 2, y + barHeight / 2); | |
| ctx.restore(); | |
| }; | |
| // Track image clicks to select specific batch image | |
| const onMouseDown = nodeType.prototype.onMouseDown; | |
| nodeType.prototype.onMouseDown = function(e, localPos, graphCanvas) { | |
| if (onMouseDown) { | |
| const r = onMouseDown.apply(this, arguments); | |
| if (r) return r; | |
| } | |
| // Check if click is in the image area | |
| if (this.imgs && this.imgs.length > 1 && localPos[1] > 0 && localPos[1] < this.size[1] - 80) { | |
| // Simple heuristic: divide image area by number of images | |
| const imgAreaHeight = this.size[1] - 80; | |
| const imgHeight = imgAreaHeight / this.imgs.length; | |
| const clickedIdx = Math.floor(localPos[1] / imgHeight); | |
| if (clickedIdx >= 0 && clickedIdx < this.imgs.length) { | |
| // Toggle selection | |
| if (this._selectedImageIndex === clickedIdx) { | |
| this._selectedImageIndex = -1; // Deselect | |
| } else { | |
| this._selectedImageIndex = clickedIdx; | |
| } | |
| this.setDirtyCanvas(true, true); | |
| } | |
| } | |
| return false; | |
| }; | |
| } | |
| }); | |