Instructions to use saik0s/comfy_backup with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- llama-cpp-python
How to use saik0s/comfy_backup with llama-cpp-python:
# !pip install llama-cpp-python from llama_cpp import Llama llm = Llama.from_pretrained( repo_id="saik0s/comfy_backup", filename="ComfyUI/models/text_encoders/gemma-3-12b-it-q2_k.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 saik0s/comfy_backup 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 saik0s/comfy_backup:Q4_K_S # Run inference directly in the terminal: llama cli -hf saik0s/comfy_backup:Q4_K_S
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf saik0s/comfy_backup:Q4_K_S # Run inference directly in the terminal: llama cli -hf saik0s/comfy_backup:Q4_K_S
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 saik0s/comfy_backup:Q4_K_S # Run inference directly in the terminal: ./llama-cli -hf saik0s/comfy_backup:Q4_K_S
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 saik0s/comfy_backup:Q4_K_S # Run inference directly in the terminal: ./build/bin/llama-cli -hf saik0s/comfy_backup:Q4_K_S
Use Docker
docker model run hf.co/saik0s/comfy_backup:Q4_K_S
- LM Studio
- Jan
- Ollama
How to use saik0s/comfy_backup with Ollama:
ollama run hf.co/saik0s/comfy_backup:Q4_K_S
- Unsloth Studio
How to use saik0s/comfy_backup 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 saik0s/comfy_backup 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 saik0s/comfy_backup to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for saik0s/comfy_backup to start chatting
- Pi
How to use saik0s/comfy_backup with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf saik0s/comfy_backup:Q4_K_S
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": "saik0s/comfy_backup:Q4_K_S" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Hermes Agent new
How to use saik0s/comfy_backup with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf saik0s/comfy_backup:Q4_K_S
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 saik0s/comfy_backup:Q4_K_S
Run Hermes
hermes
- Atomic Chat new
- Docker Model Runner
How to use saik0s/comfy_backup with Docker Model Runner:
docker model run hf.co/saik0s/comfy_backup:Q4_K_S
- Lemonade
How to use saik0s/comfy_backup with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull saik0s/comfy_backup:Q4_K_S
Run and chat with the model
lemonade run user.comfy_backup-Q4_K_S
List all available models
lemonade list
| from fractions import Fraction | |
| from typing_extensions import override | |
| from comfy_api.latest import IO, ComfyExtension, Input, InputImpl, Types | |
| from comfy_api_nodes.apis.beeble import ( | |
| CreateSwitchXRequest, | |
| SwitchXStatusResponse, | |
| ) | |
| from comfy_api_nodes.util import ( | |
| ApiEndpoint, | |
| bytesio_to_image_tensor, | |
| convert_mask_to_image, | |
| download_url_as_bytesio, | |
| download_url_to_image_tensor, | |
| download_url_to_video_output, | |
| downscale_image_tensor, | |
| downscale_video_to_max_pixels, | |
| poll_op, | |
| sync_op, | |
| upload_image_to_comfyapi, | |
| upload_video_to_comfyapi, | |
| validate_string, | |
| validate_video_frame_count, | |
| ) | |
| _MAX_PIXELS = 2_770_000 | |
| _MAX_FRAMES = 240 | |
| _MAX_PROMPT_LEN = 2000 | |
| def _validate_inputs(prompt: str | None, reference_image: Input.Image | None) -> str | None: | |
| """Beeble requires at least one of prompt or reference_image. Returns the cleaned prompt.""" | |
| cleaned = prompt.strip() if prompt else "" | |
| if not cleaned and reference_image is None: | |
| raise ValueError("At least one of 'prompt' or 'reference_image' must be provided.") | |
| if cleaned: | |
| validate_string(cleaned, strip_whitespace=False, max_length=_MAX_PROMPT_LEN) | |
| return cleaned or None | |
| async def _upload_mask_as_image( | |
| cls: type[IO.ComfyNode], | |
| mask: Input.Image, | |
| *, | |
| wait_label: str, | |
| ) -> str: | |
| """Encode a single-frame MASK (H, W) or (1, H, W) as a PNG and upload.""" | |
| if mask.dim() == 2: | |
| mask = mask.unsqueeze(0) | |
| image = convert_mask_to_image(mask[:1]) | |
| return await upload_image_to_comfyapi( | |
| cls, | |
| image, | |
| mime_type="image/png", | |
| wait_label=wait_label, | |
| total_pixels=_MAX_PIXELS, | |
| ) | |
| async def _upload_mask_batch_as_video( | |
| cls: type[IO.ComfyNode], | |
| mask: Input.Image, | |
| *, | |
| frame_rate: Fraction, | |
| source_frame_count: int, | |
| wait_label: str, | |
| ) -> str: | |
| """Encode a MASK batch (N, H, W) as a grayscale H.264 MP4 at frame_rate and upload. | |
| The matte is always downscaled to the pixel budget so it stays within Beeble's limit and | |
| keeps the same dimensions as the (similarly downscaled) source — both use the same algorithm | |
| from the same starting dimensions, and downscaling is a no-op when already within budget. | |
| """ | |
| if mask.dim() == 2: | |
| mask = mask.unsqueeze(0) | |
| if mask.shape[0] != source_frame_count: | |
| raise ValueError( | |
| f"Custom alpha video frame count ({mask.shape[0]}) does not match the " | |
| f"source video frame count ({source_frame_count}). The Beeble API requires " | |
| "one mask per source frame." | |
| ) | |
| images = downscale_image_tensor(convert_mask_to_image(mask), _MAX_PIXELS) | |
| alpha_video = InputImpl.VideoFromComponents(Types.VideoComponents(images=images, audio=None, frame_rate=frame_rate)) | |
| return await upload_video_to_comfyapi(cls, alpha_video, wait_label=wait_label) | |
| def _alpha_mode_input(*, video: bool) -> IO.DynamicCombo.Input: | |
| """Build the alpha_mode DynamicCombo with mode-specific extra inputs.""" | |
| select_keyframe_tooltip = ( | |
| "First-frame keyframe mask. Beeble propagates this across the video." if video else "Grayscale keyframe mask." | |
| ) | |
| custom_tooltip = ( | |
| "Per-frame grayscale mask covering the entire video. " | |
| "Must have the same frame count as the source. " | |
| "Connect a MASK output from SAM3_TrackToMask or similar." | |
| if video | |
| else "Grayscale mask to apply." | |
| ) | |
| return IO.DynamicCombo.Input( | |
| "alpha_mode", | |
| tooltip=( | |
| "Controls how SwitchX decides what to keep vs. regenerate. " | |
| "'auto' isolates the main subject automatically. " | |
| "'fill' regenerates the entire frame while preserving geometry. " | |
| "'select' propagates a first-frame keyframe across the clip. " | |
| "'custom' uses a per-frame alpha matte you provide." | |
| ), | |
| options=[ | |
| IO.DynamicCombo.Option("auto", []), | |
| IO.DynamicCombo.Option("fill", []), | |
| IO.DynamicCombo.Option( | |
| "select", | |
| [IO.Mask.Input("alpha_keyframe", tooltip=select_keyframe_tooltip)], | |
| ), | |
| IO.DynamicCombo.Option( | |
| "custom", | |
| [IO.Mask.Input("alpha_mask", tooltip=custom_tooltip)], | |
| ), | |
| ], | |
| ) | |
| def _common_inputs(*, source: IO.Input, video: bool) -> list[IO.Input]: | |
| return [ | |
| source, | |
| IO.String.Input( | |
| "prompt", | |
| multiline=True, | |
| default="", | |
| tooltip=( | |
| "Text description of the desired output (max 2000 chars). " | |
| "At least one of 'prompt' or 'reference_image' is required." | |
| ), | |
| ), | |
| IO.Image.Input( | |
| "reference_image", | |
| optional=True, | |
| tooltip=( | |
| "Reference image whose look (background, lighting, costume) the result " | |
| "should adopt. At least one of 'reference_image' or 'prompt' is required." | |
| ), | |
| ), | |
| _alpha_mode_input(video=video), | |
| IO.Combo.Input( | |
| "max_resolution", | |
| options=["1080p", "720p"], | |
| default="1080p", | |
| tooltip="Maximum output resolution.", | |
| ), | |
| IO.Int.Input( | |
| "seed", | |
| default=0, | |
| min=0, | |
| max=2147483647, | |
| control_after_generate=True, | |
| tooltip=( | |
| "Seed controls whether the node should re-run; " "results are non-deterministic regardless of seed." | |
| ), | |
| ), | |
| ] | |
| async def _submit_and_poll( | |
| cls: type[IO.ComfyNode], | |
| request: CreateSwitchXRequest, | |
| ) -> SwitchXStatusResponse: | |
| initial = await sync_op( | |
| cls, | |
| ApiEndpoint(path="/proxy/beeble/v1/switchx/generations", method="POST"), | |
| response_model=SwitchXStatusResponse, | |
| data=request, | |
| ) | |
| return await poll_op( | |
| cls, | |
| ApiEndpoint(path=f"/proxy/beeble/v1/switchx/generations/{initial.id}"), | |
| response_model=SwitchXStatusResponse, | |
| status_extractor=lambda r: r.status, | |
| progress_extractor=lambda r: r.progress, | |
| ) | |
| def _require_output_url(response: SwitchXStatusResponse, name: str) -> str: | |
| if response.output is None or getattr(response.output, name) is None: | |
| raise RuntimeError(f"Beeble job {response.id} completed without a {name!r} output URL.") | |
| return getattr(response.output, name) | |
| def _alpha_url(response: SwitchXStatusResponse, mode: str) -> str | None: | |
| """URL of the alpha matte, or None when the mode produces no separate matte. | |
| 'fill' selects the whole frame, so Beeble writes no alpha asset even though the status | |
| response still returns a (dangling) signed URL for it — fetching it 403s with S3 | |
| AccessDenied. The other three modes ('auto', 'custom', 'select') all produce a real, | |
| downloadable matte. | |
| """ | |
| if mode == "fill" or response.output is None: | |
| return None | |
| return response.output.alpha | |
| class BeebleSwitchXVideoEdit(IO.ComfyNode): | |
| def define_schema(cls) -> IO.Schema: | |
| return IO.Schema( | |
| node_id="BeebleSwitchXVideoEdit", | |
| display_name="Beeble SwitchX Video Edit", | |
| category="partner/video/Beeble", | |
| description=( | |
| "Edit a video with Beeble SwitchX. Switches anything in the scene (background, " | |
| "lighting, costume) while preserving the original subject's pixels and motion. " | |
| "Provide a reference image and/or text prompt to describe the new look. " | |
| "Max 240 frames, max ~2.77MP per frame." | |
| ), | |
| inputs=_common_inputs(source=IO.Video.Input("video"), video=True), | |
| outputs=[ | |
| IO.Video.Output(display_name="video"), | |
| IO.Video.Output( | |
| display_name="alpha", | |
| tooltip="The alpha matte Beeble used. Empty for 'fill' mode, which has no separate matte.", | |
| ), | |
| ], | |
| hidden=[ | |
| IO.Hidden.auth_token_comfy_org, | |
| IO.Hidden.api_key_comfy_org, | |
| IO.Hidden.unique_id, | |
| ], | |
| is_api_node=True, | |
| price_badge=IO.PriceBadge( | |
| depends_on=IO.PriceBadgeDepends(widgets=["max_resolution"]), | |
| expr=""" | |
| ( | |
| $rate := widgets.max_resolution = "1080p" ? 0.429 : 0.143; | |
| {"type":"usd","usd": $rate, "format":{"suffix":"/30 frames"}} | |
| ) | |
| """, | |
| ), | |
| ) | |
| async def execute( | |
| cls, | |
| video: Input.Video, | |
| prompt: str, | |
| alpha_mode: dict, | |
| max_resolution: str, | |
| seed: int, | |
| reference_image: Input.Image | None = None, | |
| ) -> IO.NodeOutput: | |
| cleaned_prompt = _validate_inputs(prompt, reference_image) | |
| validate_video_frame_count(video, max_frame_count=_MAX_FRAMES) | |
| video = downscale_video_to_max_pixels(video, _MAX_PIXELS) | |
| mode = alpha_mode["alpha_mode"] | |
| alpha_uri: str | None = None | |
| if mode == "select": | |
| alpha_uri = await _upload_mask_as_image(cls, alpha_mode["alpha_keyframe"], wait_label="Uploading keyframe") | |
| elif mode == "custom": | |
| alpha_uri = await _upload_mask_batch_as_video( | |
| cls, | |
| alpha_mode["alpha_mask"], | |
| frame_rate=video.get_frame_rate(), | |
| source_frame_count=video.get_frame_count(), | |
| wait_label="Uploading alpha video", | |
| ) | |
| source_uri = await upload_video_to_comfyapi(cls, video, wait_label="Uploading source") | |
| reference_uri: str | None = None | |
| if reference_image is not None: | |
| reference_uri = await upload_image_to_comfyapi( | |
| cls, | |
| reference_image, | |
| mime_type="image/png", | |
| wait_label="Uploading reference", | |
| total_pixels=_MAX_PIXELS, | |
| ) | |
| request = CreateSwitchXRequest( | |
| generation_type="video", | |
| source_uri=source_uri, | |
| alpha_mode=mode, | |
| prompt=cleaned_prompt, | |
| reference_image_uri=reference_uri, | |
| alpha_uri=alpha_uri, | |
| max_resolution=1080 if max_resolution == "1080p" else 720, | |
| ) | |
| response = await _submit_and_poll(cls, request) | |
| render = await download_url_to_video_output(_require_output_url(response, "render")) | |
| alpha = None | |
| if (alpha_url := _alpha_url(response, mode)) is not None: | |
| alpha = await download_url_to_video_output(alpha_url) | |
| return IO.NodeOutput(render, alpha) | |
| class BeebleSwitchXImageEdit(IO.ComfyNode): | |
| def define_schema(cls) -> IO.Schema: | |
| return IO.Schema( | |
| node_id="BeebleSwitchXImageEdit", | |
| display_name="Beeble SwitchX Image Edit", | |
| category="partner/image/Beeble", | |
| description=( | |
| "Edit a single image with Beeble SwitchX. Switches anything in the scene " | |
| "(background, lighting, costume) while preserving the original subject's pixels. " | |
| "Provide a reference image and/or text prompt to describe the new look. " | |
| "Max ~2.77MP." | |
| ), | |
| inputs=_common_inputs(source=IO.Image.Input("image"), video=False), | |
| outputs=[ | |
| IO.Image.Output(display_name="image"), | |
| IO.Mask.Output( | |
| display_name="alpha", | |
| tooltip="The alpha matte Beeble used. Empty for 'fill' mode, which has no separate matte.", | |
| ), | |
| ], | |
| hidden=[ | |
| IO.Hidden.auth_token_comfy_org, | |
| IO.Hidden.api_key_comfy_org, | |
| IO.Hidden.unique_id, | |
| ], | |
| is_api_node=True, | |
| price_badge=IO.PriceBadge( | |
| depends_on=IO.PriceBadgeDepends(widgets=["max_resolution"]), | |
| expr=""" | |
| ( | |
| $rate := widgets.max_resolution = "1080p" ? 0.429 : 0.143; | |
| {"type":"usd","usd": $rate} | |
| ) | |
| """, | |
| ), | |
| ) | |
| async def execute( | |
| cls, | |
| image: Input.Image, | |
| prompt: str, | |
| alpha_mode: dict, | |
| max_resolution: str, | |
| seed: int, | |
| reference_image: Input.Image | None = None, | |
| ) -> IO.NodeOutput: | |
| cleaned_prompt = _validate_inputs(prompt, reference_image) | |
| image = downscale_image_tensor(image, _MAX_PIXELS) | |
| mode = alpha_mode["alpha_mode"] | |
| alpha_uri: str | None = None | |
| if mode == "select": | |
| alpha_uri = await _upload_mask_as_image(cls, alpha_mode["alpha_keyframe"], wait_label="Uploading keyframe") | |
| elif mode == "custom": | |
| alpha_uri = await _upload_mask_as_image(cls, alpha_mode["alpha_mask"], wait_label="Uploading alpha") | |
| source_uri = await upload_image_to_comfyapi( | |
| cls, | |
| image, | |
| mime_type="image/png", | |
| wait_label="Uploading source", | |
| total_pixels=None, | |
| ) | |
| reference_uri: str | None = None | |
| if reference_image is not None: | |
| reference_uri = await upload_image_to_comfyapi( | |
| cls, | |
| reference_image, | |
| mime_type="image/png", | |
| wait_label="Uploading reference", | |
| total_pixels=_MAX_PIXELS, | |
| ) | |
| request = CreateSwitchXRequest( | |
| generation_type="image", | |
| source_uri=source_uri, | |
| alpha_mode=mode, | |
| prompt=cleaned_prompt, | |
| reference_image_uri=reference_uri, | |
| alpha_uri=alpha_uri, | |
| max_resolution=1080 if max_resolution == "1080p" else 720, | |
| ) | |
| response = await _submit_and_poll(cls, request) | |
| render = await download_url_to_image_tensor(_require_output_url(response, "render")) | |
| alpha_mask = None | |
| if (alpha_url := _alpha_url(response, mode)) is not None: | |
| alpha_image = bytesio_to_image_tensor(await download_url_as_bytesio(alpha_url), mode="L") | |
| alpha_mask = alpha_image.squeeze(-1) if alpha_image.dim() == 4 else alpha_image | |
| return IO.NodeOutput(render, alpha_mask) | |
| class BeebleExtension(ComfyExtension): | |
| async def get_node_list(self) -> list[type[IO.ComfyNode]]: | |
| return [ | |
| BeebleSwitchXVideoEdit, | |
| BeebleSwitchXImageEdit, | |
| ] | |
| async def comfy_entrypoint() -> BeebleExtension: | |
| return BeebleExtension() | |