$ cat node-template.py

S

SVG Generator

// Generate a vector SVG illustration or icon from a text prompt, or vectorize a connected raster image.

AI
Image
#svg#vector#illustration#icon#vectorize
template.py
1"""svg-generator — text-to-SVG or raster-to-SVG (gais-svg-creation).23Two modes, picked by what is connected:4- an ``image`` is connected  -> vectorize that raster image (prompt ignored);5- otherwise                  -> generate from the ``prompt`` (required).67Writes the SVG to /data/output/image.svg (``svg`` port, Image family, so the8canvas previews it) and also returns the raw markup on ``svg_code`` for9chaining into text/LLM/slide nodes. Privileged transport lives in the gais10SDK; no os/pathlib/HTTP here (lint-blocked).11"""1213from __future__ import annotations1415import json16import sys17import traceback18from typing import Any1920from gais import Gais2122INPUT_DIR = "/data/input"23OUTPUT_DIR = "/data/output"2425OUTPUT_FILENAME = "image.svg"2627TEMPERATURE_MIN = 0.128TEMPERATURE_MAX = 1.0293031def _input_file_path(inputs: dict[str, Any], key: str) -> str | None:32    """Upstream file ports deliver a bare filename under /data/input."""33    name = inputs.get(key)34    if not name or not isinstance(name, str):35        return None36    base = name.replace("\\", "/").rsplit("/", 1)[-1].strip()37    if not base or base in (".", ".."):38        return None39    return f"{INPUT_DIR}/{base}"404142def _parse_temperature(raw: Any) -> float | None:43    """None/empty -> backend default; otherwise clamp into [0.1, 1.0]."""44    if raw is None or (isinstance(raw, str) and not raw.strip()):45        return None46    try:47        value = float(raw)48    except (TypeError, ValueError):49        raise ValueError(f"Temperature must be a number, got {raw!r}") from None50    return min(max(value, TEMPERATURE_MIN), TEMPERATURE_MAX)515253def _parse_seed(raw: Any) -> int | None:54    """None/empty/negative -> None (random seed); otherwise an int."""55    if raw is None or isinstance(raw, bool):56        return None57    if isinstance(raw, str) and not raw.strip():58        return None59    try:60        value = int(float(raw))61    except (TypeError, ValueError):62        raise ValueError(f"Seed must be an integer, got {raw!r}") from None63    return value if value >= 0 else None646566def process(inputs: dict[str, Any]) -> dict[str, Any]:67    image_path = _input_file_path(inputs, "image")68    prompt_raw = inputs.get("prompt")69    prompt = prompt_raw.strip() if isinstance(prompt_raw, str) else ""70    temperature = _parse_temperature(inputs.get("temperature"))71    seed = _parse_seed(inputs.get("seed"))7273    if image_path is not None:74        if prompt:75            print(76                "[svg-generator] image connected: vectorizing it, prompt ignored",77                file=sys.stderr,78            )79        mode = "vectorize"80        result = Gais.svg.vectorize(image=image_path, temperature=temperature, seed=seed)81    else:82        if not prompt:83            raise ValueError(84                "Provide a prompt to generate an SVG, or connect an image to vectorize"85            )86        mode = "create"87        result = Gais.svg.create(prompt=prompt, temperature=temperature, seed=seed)8889    svg_bytes = result.content or b""90    svg_code = result.text or svg_bytes.decode("utf-8", errors="replace")91    if not svg_bytes and svg_code:92        svg_bytes = svg_code.encode("utf-8")93    if not svg_bytes:94        raise RuntimeError("gais-svg-creation returned an empty SVG")9596    with open(f"{OUTPUT_DIR}/{OUTPUT_FILENAME}", "wb") as fh:97        fh.write(svg_bytes)9899    meta = result.metadata or {}100    print(101        f"[svg-generator] mode={mode} "102        f"time={meta.get('inference_time_ms', 'unknown')}ms "103        f"model_size={meta.get('model_size', 'unknown')} "104        f"seed={meta.get('seed', 'unknown')} "105        f"subtype={meta.get('subtype', 'unknown')} "106        f"candidates_valid={meta.get('candidates_valid', 'unknown')} "107        f"bytes={len(svg_bytes)}",108        file=sys.stderr,109    )110111    return {"svg": OUTPUT_FILENAME, "svg_code": svg_code}112113114def main() -> None:115    try:116        envelope = json.loads(sys.stdin.read() or "{}")117        if not isinstance(envelope, dict):118            envelope = {}119        inputs = envelope.get("inputs", {}) or {}120        json.dump(process(inputs), sys.stdout)121    except Exception as e:122        print(123            json.dumps({124                "error": str(e),125                "errorType": type(e).__name__,126                "traceback": traceback.format_exc(),127            }),128            file=sys.stderr,129        )130        sys.exit(1)131132133if __name__ == "__main__":134    main()135

$ git log --oneline

v1.0.0
HEAD
2026-09-28

New: text-to-SVG / image-to-SVG via the gais svg.create / svg.vectorize ops (PR #1340 feat/svg-creation)