Skill: robin797860/qwen-image (version 1.0.0)

--- name: qwen-image description: Generate images using Qwen Image API (Alibaba Cloud DashScope). Use when users request image generation with Chinese prompts or need high-quality AI-generated images from text descriptions. homepage: https://dashscope.aliyuncs.com/ metadata: {"openclaw":{"emoji":"🎨","requires":{"bins":["uv"]},"install":[{"id":"uv-brew","kind":"brew","formula":"uv","bins":["uv"],"label":"Install uv (brew)"}]}} --- # Qwen Image Generate high-quality images using Alibaba Cloud's Qwen Image API (通义万相). ## Usage Generate an image (returns URL only): ```bash uv run {baseDir}/scripts/generate_image.py --prompt "一副典雅庄重的对联悬挂于厅堂之中" --size "1664*928" --api-key sk-xxx ``` Generate and save locally: ```bash uv run {baseDir}/scripts/generate_image.py --prompt "一副典雅庄重的对联悬挂于厅堂之中" --size "1664*928" --api-key sk-xxx ``` With custom model: Support `qwen-image-max-2025-12-30` `qwen-image-plus-2026-01-09` `qwen-image-plus` ```bash uv run {baseDir}/scripts/generate_image.py --prompt "a beautiful sunset over mountains" --model qwen-image-plus-2026-01-09 --api-key sk-xxx ``` ## API Key You can obtain the API key and run the image generation command in the following order. - Get apiKey from `models.providers.bailian.apiKey` in `~/.openclaw/openclaw.json` - Or get from `skills."qwen-image".apiKey` in `~/.openclaw/openclaw.json` - Or get from `DASHSCOPE_API_KEY` environment variable - Or Get your API key from: https://dashscope.console.aliyun.com/ ## Options **Sizes:** - `1664*928` (default) - 16:9 landscape - `1024*1024` - Square format - `720*1280` - 9:16 portrait - `1280*720` - 16:9 landscape (smaller) **Additional flags:** - `--negative-prompt "unwanted elements"` - Specify what to avoid - `--no-prompt-extend` - Disable automatic prompt enhancement - `--watermark` - Add watermark to generated image - `--no-verify-ssl` - Disable SSL certificate verification (use when behind corporate proxy) ## Workflow 1. Execute the generate_image.py script with the user's prompt 2. Parse the script output and find the line starting with `MEDIA_URL:` 3. Extract the image URL from that line (format: `MEDIA_URL: https://...`) 4. Display the image to the user using markdown syntax: `![Generated Image](URL)` 5. Do NOT download or save the image unless the user specifically requests it ## Notes - Supports both Chinese and English prompts - By default, returns image URL directly without downloading - The script prints `MEDIA_URL:` in the output - extract this URL and display it using markdown image syntax: `![generated image](URL)` - Always look for the line starting with `MEDIA_URL:` in the script output and render the image for the user - Default negative prompt helps avoid common AI artifacts - Images are hosted on Alibaba Cloud OSS with temporary access URLs

path: scripts/generate_image.py
content: #!/usr/bin/env python3
# /// script
# requires-python = ">=3.10"
# dependencies = [
#     "requests>=2.31.0",
# ]
# ///
"""
Generate images using Qwen Image API (Alibaba Cloud DashScope).

Usage:
    uv run generate_image.py --prompt "your image description" --filename "output.png" [--model qwen-image-max|qwen-image-turbo] [--size 1664*928|1024*1024|720*1280|1280*720] [--api-key KEY]
"""

import argparse
import os
import sys
import json
import base64
from pathlib import Path


def get_api_key(provided_key: str | None) -> str | None:
    """Get API key from argument first, then environment."""
    if provided_key:
        return provided_key
    return os.environ.get("DASHSCOPE_API_KEY")


def main():
    parser = argparse.ArgumentParser(
        description="Generate images using Qwen Image API"
    )
    parser.add_argument(
        "--prompt", "-p",
        required=True,
        help="Image description/prompt"
    )
    parser.add_argument(
        "--filename", "-f",
        help="Output filename (optional, if not provided will only return URL)"
    )
    parser.add_argument(
        "--model", "-m",
        choices=["qwen-image-max", "qwen-image-turbo", "qwen-image-plus-2026-01-09"],
        default="qwen-image-max",
        help="Model to use: qwen-image-max (default) or qwen-image-turbo"
    )
    parser.add_argument(
        "--size", "-s",
        choices=["1664*928", "1024*1024", "720*1280", "1280*720"],
        default="1664*928",
        help="Output size (default: 1664*928 for 16:9 ratio)"
    )
    parser.add_argument(
        "--negative-prompt", "-n",
        default="低分辨率，低画质，肢体畸形，手指畸形，画面过饱和，蜡像感，人脸无细节，过度光滑，画面具有AI感。构图混乱。文字模糊，扭曲。",
        help="Negative prompt to avoid unwanted elements"
    )
    parser.add_argument(
        "--no-prompt-extend",
        action="store_true",
        help="Disable automatic prompt enhancement"
    )
    parser.add_argument(
        "--watermark",
        action="store_true",
        help="Add watermark to generated image"
    )
    parser.add_argument(
        "--api-key", "-k",
        help="DashScope API key (overrides DASHSCOPE_API_KEY env var)"
    )
    parser.add_argument(
        "--no-verify-ssl",
        action="store_true",
        help="Disable SSL certificate verification (use with caution)"
    )

    args = parser.parse_args()

    # Get API key
    [REDACTED_SECRET])
    if not [REDACTED_SECRET]"Error: No API key provided.", file=sys.stderr)
        print("Please either:", file=sys.stderr)
        print("  1. Provide --api-key argument", file=sys.stderr)
        print("  2. Set DASHSCOPE_API_KEY environment variable", file=sys.stderr)
        sys.exit(1)

    # Import here after checking API key
    import requests

    # Set up output path

    # Build request payload
    payload = {
        "model": args.model,
        "input": {
            "messages": [
                {
                    "role": "user",
                    "content": [
                        {
                            "text": args.prompt
                        }
                    ]
                }
            ]
        },
        "parameters": {
            "negative_prompt": args.negative_prompt,
            "prompt_extend": not args.no_prompt_extend,
            "watermark": args.watermark,
            "size": args.size
        }
    }

    print(f"Generating image with {args.model}...")
    print(f"Size: {args.size}")
    print(f"Prompt: {args.prompt}")

    try:
        # Make API request
        response = requests.post(
            "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
            headers={
                "Content-Type": "application/json",
                "Authorization": f"Bearer {api_key}"
            },
            json=payload,
            timeout=120,
            verify=True
        )

        response.raise_for_status()
        result = response.json()

        # Check for errors
        if result.get("code"):
            error_msg = result.get("message", "Unknown error")
            print(f"API Error: {error_msg}", file=sys.stderr)
            sys.exit(1)

        # Extract image URL from response
        output_data = result.get("output", {})
        choices = output_data.get("choices", [])
        
        if not choices:
            print("Error: No choices in response", file=sys.stderr)
            print(f"Response: {json.dumps(result, indent=2, ensure_ascii=False)}", file=sys.stderr)
            sys.exit(1)

        # Get image URL from first choice
        message = choices[0].get("message", {})
        content = message.get("content", [])
        
        if not content or not content[0].get("image"):
            print("Error: No image URL in response", file=sys.stderr)
            print(f"Response: {json.dumps(result, indent=2, ensure_ascii=False)}", file=sys.stderr)
            sys.exit(1)

        image_url = content[0]["image"]
        print(f"\nImage URL: {image_url}")

        # If filename is provided, download and save the image
        if args.filename:
            output_path = Path(args.filename)
            output_path.parent.mkdir(parents=True, exist_ok=True)
            
            print("Downloading image...")
            img_response = requests.get(image_url, timeout=30, verify=not args.no_verify_ssl)
            img_response.raise_for_status()

            # Save the image
            with open(output_path, "wb") as f:
                f.write(img_response.content)

            full_path = output_path.resolve()
            print(f"Image saved: {full_path}")
            # Clawdbot parses MEDIA tokens and will attach the file on supported providers.
            print(f"MEDIA: {full_path}")
        else:
            # Just return the URL for Clawdbot to display
            print(f"MEDIA_URL: {image_url}")

    except requests.exceptions.HTTPError as e:
        print(f"HTTP Error: {e}", file=sys.stderr)
        try:
            error_detail = response.json()
            print(f"Error details: {json.dumps(error_detail, indent=2, ensure_ascii=False)}", file=sys.stderr)
        except:
            print(f"Response text: {response.text}", file=sys.stderr)
        sys.exit(1)
    except requests.exceptions.RequestException as e:
        print(f"Error making API request: {e}", file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(f"Error generating image: {e}", file=sys.stderr)
        sys.exit(1)


if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: suspicious | SkillSpector: suspicious (MEDIUM)