Skip to main content

g6-adapt-image

Use when you need to resize, crop, rotate, flip, recolour, combine or convert a picture, measure one strictly, or have one generated (locally through ComfyUI or a procedural technique, or by a paid image service only when G6's action gate allows the payment), and you want every resulting file checked before you use it.

Source facts

Repository
basicisabc1994/g6solver
Last source activity
October 30, 2026 at 10:47
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
g6-adapt-image
description
Use when you need to resize, crop, rotate, flip, recolour, combine or convert a picture, measure one strictly, or have one generated (locally through ComfyUI or a procedural technique, or by a paid image service only when G6's action gate allows the payment), and you want every resulting file checked before you use it.
# G6 adapt_image `g6.media.adapt_image` edits and measures pictures with Pillow, and generates them through the media family's registered image backends. Every picture it makes is written into G6's media folder (`$G6_HOME/media`) and inspected from disk against the size it must have: it decodes, is the format and size asked for, and is not blank. A paid image service is used only from `generate_paid`, for a backend the person registered, and only when G6's action gate lets that payment through: the person's key file (`~/.g6/.env`) holding the backend's key is their approval to spend it. Without it the reply is a dry run. Its best state is `qualified-draft`. ## When to use it - You (a coding agent) need a thumbnail, a crop, a rotated or flipped copy, a lighter or more saturated version, a logo placed on a picture, or the same picture as JPEG or WebP, and you want to know the file is what you asked for. - You need the true format, size, mode and frame count of a picture, read from its bytes, not from its name. - You want a picture generated without choosing the backend yourself: a prompt goes to the person's ComfyUI, a named technique to the `procedural` backend, and nothing goes to a paid service unless the action gate lets it through. ## When not to use it - Fetching a picture from the web: there is no URL input. Put the picture in the media folder, or send its bytes as base64 (at most 25 MiB). - Judging what a picture shows: nothing here does. The optional vision check (from Python) records a model's description as a claim, never as a check. - Upscaling with a model, image-to-image or inpainting: use `adapt_comfyui`. `resize` here is Lanczos resampling. - Sound, video, diagrams or charts: other media extensions. ## Ops - `resize`: `width`, `height` or both (one side alone keeps the aspect ratio), by Lanczos resampling. - `crop`: `box` `[left, top, right, bottom]`, which must lie inside the picture (it is never padded). - `rotate`: `angle` in degrees, counter-clockwise; a right angle turns exactly, any other grows the canvas to hold the picture (that size is not compared). - `flip`: `direction` `horizontal` or `vertical`. - `adjust_colour`: `brightness`, `contrast` and `saturation` as factors from 0 to 4, 1 for no change. - `composite`: `overlay` (or `overlay_base64`) placed on the picture at `x`, `y`. - `convert`: the same picture as `format` `png`, `jpeg` or `webp` (`quality` 1 to 100); a JPEG has no transparency, so a transparent picture is laid on white. - `get_image_info`: the picture decoded in full and measured: format and MIME type from its bytes, width, height, mode, frames, bytes, SHA-256. Nothing is written. - `generate`: a picture from a local backend: `backend` named, or `comfyui` with a `prompt` and `procedural` without (technique and parameters in `options`); `width`, `height`, `seed`. It never starts Blender: use `adapt_blender`. - `generate_paid`: a picture from the paid `backend` the person registered, from `prompt`. Each request is a payment `g6.action_gating` decides first: it sends on `allow`, or on `allow_with_notice` with `G6_E_KEY_FILE_APPROVED` (the key file holds the backend's key); otherwise nothing is sent and the reply is the dry run with the gate's decision and message (such as `G6_E_KEY_MISSING` and the line to add). `allow_paid` is your word: without it nothing is sent; with it nothing more opens. - `backends`: the image backends registered and what each is (local, network, paid), without asking any of them. Every op that takes a picture takes `source` (a file name in the media folder) or `image_base64`, one of them. Every op that makes one takes `name` (G6 chooses the folder) and `overwrite`; the edits also take `format` and `quality`. ## Alone and composed Alone over MCP: `g6_invoke("adapt_image", "resize", '{"source": "photo.png", "width": 320}')`, then open `result.artifact.path`. The edits, `get_image_info` and `backends` run over MCP as they are. `generate` may run a workflow on the person's ComfyUI and `generate_paid` spends money, so both are sensitive: over MCP each runs only when the person who runs the server lists it (`adapt_image.generate`, `adapt_image.generate_paid`) in `G6_MCP_SENSITIVE`. Ask them; never set it yourself, never write their key file, and never send `allow_paid` without their agreement. Even then, `generate_paid` sends only when the action gate lets it through. Composed: - A picture from `adapt_comfyui` or `adapt_generative_art` is a `source` here by its file name. - `action_gating` decides every `generate_paid` request as a `payment` before anything is sent, and keeps its audit record under `$G6_HOME`. - A paid backend exists only when the person registers one before starting G6, its key in their key file: `image_backends.register(StabilityBackend.from_key_file(model=...))` (`STABILITY_API_KEY`; also `ReplicateBackend`, `REPLICATE_API_TOKEN`, and `ImagesEndpointBackend` for an OpenAI-compatible images endpoint, `G6_IMAGES_API_KEY`, from `g6.media.adapt_image.paid`). `g6_describe` lists these keys. - From Python, `AdaptImageBlock(vision=caller)` with an image-capable `g6.core.llm` caller and `vision_check: true` adds the model's description of the result to the evidence as a claim. ## Failure modes it catches - `visual_check_failed` (an image, video or 3D output that is blank, corrupt or wrong, reported as done): every file written is inspected against the size computed for it. A black result (brightness 0), a crop of an empty margin or a paid service's uniform picture is refused with `G6_E_MEDIA_BLANK`; a service that ignored the size asked for with `G6_E_MEDIA_SIZE_MISMATCH`; one that sent another format with `G6_E_MEDIA_FORMAT_MISMATCH`. The file is removed. - `unchosen_paid_service` (a paid outside service used without the person choosing it): `generate` never uses a paid or online backend and never falls back to one; `generate_paid` needs a backend the person registered, over MCP the server's `G6_MCP_SENSITIVE`, and the action gate, which lets the payment through only when the person's key file holds the backend's key. `allow_paid` is recorded as a claim: a caller's word opens nothing. ## Reading the result - A result is `qualified-draft`: `G6_E_MEDIA_INSPECTED` for a picture made (its `artifact` holds the path, the MIME type read from the bytes, the checks, measurements and provenance), `G6_E_MEDIA_MEASURED` for `get_image_info`, `G6_E_MEDIA_DESCRIBED` for `backends`. - A refusal is `blocked-escalated` with its code: the inspection codes above, `G6_E_MEDIA_UNDECODABLE` (the source is not a whole picture in PNG, JPEG, WebP, GIF, BMP or TIFF; a format another program would draw, such as EPS or WMF, is never opened), `G6_E_MEDIA_INPUT_INVALID` (a box outside the picture, a picture given or a size asked for of more than 4096 x 4096 pixels, refused before anything is decoded, bad base64, a missing file), `G6_E_MEDIA_PAID_NOT_ALLOWED` (nothing was sent; the `action_gate` evidence says why and what the person must do), `G6_E_MEDIA_BACKEND_UNAVAILABLE`, `G6_E_MEDIA_BACKEND_FAILED` (a service's error, a content filter, a redirect to a private address) or `G6_E_MEDIA_EXTRA_MISSING`. ## Blind spots - The inspector measures; it does not look. A crop of the wrong region or a paid picture of the wrong subject passes. - Blank means nearly one colour; a washed-out or noisy result passes. The smallest detail it can see covers 0.1 % of the picture (about 130 x 130 pixels in a 4096 x 4096 one). A picture longer than 1024 pixels on a side is measured on a copy reduced by a whole factor (4 at 4096), each pixel the mean of a block, so lines thinner than that are averaged with what surrounds them and faint ones are not seen. - An animation is read as its first frame. A JPEG or WebP written at low `quality` is lossy and still passes. - A key in the key file approves every paid request to its service, with no spending cap: what a paid picture costs is not known to G6 (the service's own account says). Remove the key to stop it. - A vision model's description is a claim: it is not compared with anything and never changes a label. ## What it needs - `pip install "g6[image] @ git+https://github.com/basicisabc1994/g6solver"` for Pillow (MIT-CMU). Without it, every op that reads or makes a picture refuses with `G6_E_MEDIA_EXTRA_MISSING`; `backends`, `ops` and `help` still answer. - For `generate` with a prompt: the person's ComfyUI, through `adapt_comfyui`'s `comfyui` backend. - For `generate_paid`: an account with Stability AI, Replicate or an OpenAI-compatible images service, and its key in the person's key file, which they write themselves. Nothing here reads a key from anywhere else. ## Examples A small picture measured from its bytes. ```g6-example op=get_image_info {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC"} ``` The same picture enlarged to 64 pixels wide (the height follows), as a WebP. ```g6-example op=resize {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "width": 64, "format": "webp", "name": "gradient-wide"} ``` Its left half. ```g6-example op=crop {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "box": [0, 0, 8, 12]} ``` The picture turned a quarter, counter-clockwise (16 by 12 becomes 12 by 16). ```g6-example op=rotate {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "angle": 90} ``` Mirrored left to right. ```g6-example op=flip {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "direction": "horizontal"} ``` Brighter and more saturated. ```g6-example op=adjust_colour {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "brightness": 1.2, "saturation": 1.5} ``` A small red square laid on it, 4 pixels in from the top left corner. ```g6-example op=composite {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "overlay_base64": "iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAIAAAAmkwkpAAAAFElEQVR42mO8IyfHAANMDEgANwcAOHIBID2Vy4IAAAAASUVORK5CYII=", "x": 4, "y": 4} ``` The same picture as a JPEG. ```g6-example op=convert {"image_base64": "iVBORw0KGgoAAAANSUhEUgAAABAAAAAMCAIAAADkharWAAAAG0lEQVR42mNkYGgQYGAgHrEwiDCQBEY1jBQNAGacAnoKHKLTAAAAAElFTkSuQmCC", "format": "jpeg", "quality": 85} ``` What backends there are. ```g6-example op=backends {} ``` A picture from a prompt on the person's ComfyUI (sensitive). ```g6-example op=generate {"prompt": "a paper boat on a pond", "options": {"model": "your-checkpoint.safetensors"}, "width": 512, "height": 512} ``` From Python: a paid request sends nothing while the person's key file lacks the backend's key, even with a backend registered and `allow_paid` (a key given in Python approves nothing). ```python from g6.core.registry import get_registry from g6.media._types import image_backends from g6.media.adapt_image.paid import PaidService, StabilityBackend image_backends.register(StabilityBackend(PaidService(key="the-persons-key", model="an-engine")), replace=True) out = get_registry().invoke("adapt_image", "generate_paid", {"backend": "stability", "prompt": "a kite", "allow_paid": True}).unwrap() assert out["completion_state"] == "blocked-escalated" and out["code"] == "G6_E_MEDIA_PAID_NOT_ALLOWED" gate = next(e for e in out["evidence"] if e["kind"] == "action_gate") assert out["evidence"][0]["kind"] == "dry_run" and gate["decision"] != "allow" image_backends.unregister("stability") ``` ## The four open problems - **Learning signals**: each refusal carries its code and the failed check; paid refusals carry the service's status. Not yet: nothing counts them across calls or per backend. - **Evaluators the model cannot influence**: the inspector reads the written file with Pillow, and the expected size is computed from the request, not read from the result; a service's success and a vision model's words are recorded, never trusted. - **Transfer by failure mode**: "blank", "wrong size" and "wrong format" carry the same codes from an edit, a local backend and a paid one. - **Enough held-out cases**: Not yet: the tests use generated pictures and recorded service replies, not a stored set of reference pictures with hashes.
View on GitHub