feat: 增加skill
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: image-generation
|
||||
description: Generate images using MiniMax API with support for custom prompts, aspect ratios, and multiple image generation.
|
||||
metadata: {"clawdbot":{"emoji":"🎨","os":["linux","darwin","win32"]}}
|
||||
---
|
||||
|
||||
# MiniMax Image Generation SKILL
|
||||
|
||||
## Description
|
||||
|
||||
Generate images using MiniMax API. Supports custom prompts, aspect ratios, generation count, and more.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
python scripts/run.py --api-key "your-api-key" --prompt "your-prompt" [options]
|
||||
```
|
||||
|
||||
## Arguments
|
||||
|
||||
| Argument | Type | Required | Default | Description |
|
||||
|----------|------|----------|---------|-------------|
|
||||
| `--api-key` | string | Yes | - | MiniMax API key (can also be set via `MINIMAX_API_KEY` env var) |
|
||||
| `--prompt` | string | Yes | - | Image generation prompt |
|
||||
| `--model` | string | No | `image-01` | Image generation model |
|
||||
| `--aspect-ratio` | string | No | `1:1` | Image aspect ratio (e.g., 16:9, 1:1, 9:16) |
|
||||
| `--response-format` | string | No | `url` | Response format (`url` or `base64`) |
|
||||
| `--n` | int | No | `1` | Number of images to generate (1-3) |
|
||||
| `--prompt-optimizer` | bool | No | `false` | Enable prompt optimizer |
|
||||
| `--subject-reference` | string | No | - | Reference image URL or local file path for image-to-image generation |
|
||||
| `--subject-type` | string | No | `character` | Subject reference type (character, product, logo, video_subject, other) |
|
||||
| `--seed` | int | No | - | Random seed for reproducible generation |
|
||||
| `--output-dir` | string | No | `./output` | Output directory for images |
|
||||
|
||||
## API Reference
|
||||
|
||||
- **Endpoint**: `POST https://api.minimaxi.com/v1/image_generation`
|
||||
- **Auth**: Bearer Token
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A sunset over the ocean"
|
||||
|
||||
# Generate multiple images
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A man in a white t-shirt, full-body, standing front view, outdoors" --n 3 --aspect-ratio "16:9"
|
||||
|
||||
# Enable prompt optimizer
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "sunset ocean" --prompt-optimizer true
|
||||
|
||||
# Image-to-image generation with reference image URL
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A girl looking into the distance from a library window" --subject-reference "https://example.com/reference.jpg" --subject-type "character" --n 2
|
||||
|
||||
# Image-to-image with local file
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A girl looking into the distance from a library window" --subject-reference "./my_character.jpg" --subject-type "character"
|
||||
|
||||
# Image-to-image with product reference
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A beautiful product shot" --subject-reference "https://example.com/product.jpg" --subject-type "product"
|
||||
|
||||
# Use seed for reproducible generation
|
||||
python scripts/run.py --api-key "sk-xxx" --prompt "A sunset over the ocean" --seed 42
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
Images are saved to the specified output directory with the naming format: `generated_image_{index}_{timestamp}.{ext}`.
|
||||
@@ -0,0 +1,287 @@
|
||||
#!/usr/bin/env python3
|
||||
# @skill: image-generation
|
||||
|
||||
"""
|
||||
MiniMax Image Generation Script
|
||||
Generate images using MiniMax API
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import time
|
||||
import requests
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
||||
def parse_args():
|
||||
"""Parse command line arguments"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Generate images using MiniMax API",
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--api-key",
|
||||
type=str,
|
||||
required=False,
|
||||
help="MiniMax API key (can also be set via MINIMAX_API_KEY environment variable)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--prompt",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Image generation prompt"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--model",
|
||||
type=str,
|
||||
default="image-01",
|
||||
help="Image generation model (default: image-01)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--aspect-ratio",
|
||||
type=str,
|
||||
default="1:1",
|
||||
help="Image aspect ratio (default: 1:1, options: 16:9, 9:16, etc.)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--response-format",
|
||||
type=str,
|
||||
default="url",
|
||||
choices=["url", "base64"],
|
||||
help="Response format (default: url)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--n",
|
||||
type=int,
|
||||
default=1,
|
||||
choices=[1, 2, 3],
|
||||
help="Number of images to generate (default: 1, max: 3)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--prompt-optimizer",
|
||||
type=lambda x: x.lower() == "true",
|
||||
default=False,
|
||||
help="Enable prompt optimizer (default: false)"
|
||||
)
|
||||
|
||||
# Image-to-image (subject reference) parameters
|
||||
parser.add_argument(
|
||||
"--subject-reference",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Reference image URL or local file path for image-to-image generation"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--subject-type",
|
||||
type=str,
|
||||
default="character",
|
||||
choices=["character", "product", "logo", "video_subject", "other"],
|
||||
help="Subject reference type (default: character)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--seed",
|
||||
type=int,
|
||||
default=None,
|
||||
help="Random seed for reproducible generation (optional)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--output-dir",
|
||||
type=str,
|
||||
default="./output",
|
||||
help="Output directory for images (default: ./output)"
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--api-base",
|
||||
type=str,
|
||||
default="https://api.minimaxi.com",
|
||||
help="API base URL (default: https://api.minimaxi.com)"
|
||||
)
|
||||
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def download_image(url: str, output_path: str) -> bool:
|
||||
"""Download image to local file"""
|
||||
try:
|
||||
response = requests.get(url, timeout=30)
|
||||
response.raise_for_status()
|
||||
|
||||
with open(output_path, "wb") as f:
|
||||
f.write(response.content)
|
||||
|
||||
print(f" [OK] Saved: {output_path}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" [FAIL] Download failed: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def read_local_image(file_path: str) -> str:
|
||||
"""
|
||||
Read local image file and return as base64 encoded string.
|
||||
Returns the base64 string or None if failed.
|
||||
"""
|
||||
try:
|
||||
with open(file_path, "rb") as f:
|
||||
image_data = f.read()
|
||||
import base64
|
||||
return base64.b64encode(image_data).decode("utf-8")
|
||||
except Exception as e:
|
||||
print(f" [FAIL] Failed to read local image: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def build_subject_reference(subject_ref: str, subject_type: str) -> dict:
|
||||
"""
|
||||
Build subject_reference object from URL or local file path.
|
||||
|
||||
Args:
|
||||
subject_ref: URL or local file path
|
||||
subject_type: Type of subject (character, product, logo, etc.)
|
||||
|
||||
Returns:
|
||||
dict with subject_reference structure
|
||||
"""
|
||||
# Check if it's a URL or local file
|
||||
if subject_ref.startswith(("http://", "https://")):
|
||||
# It's a URL
|
||||
return {
|
||||
"type": subject_type,
|
||||
"image_file": subject_ref
|
||||
}
|
||||
else:
|
||||
# It's a local file - convert to base64
|
||||
print(f" Processing local image: {subject_ref}")
|
||||
base64_data = read_local_image(subject_ref)
|
||||
if base64_data:
|
||||
return {
|
||||
"type": subject_type,
|
||||
"image_file": f"data:image/jpeg;base64,{base64_data}"
|
||||
}
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def generate_images(args):
|
||||
"""Call MiniMax API to generate images"""
|
||||
url = f"{args.api_base}/v1/image_generation"
|
||||
|
||||
headers = {
|
||||
"Authorization": f"Bearer {args.api_key}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
payload = {
|
||||
"model": args.model,
|
||||
"prompt": args.prompt,
|
||||
"aspect_ratio": args.aspect_ratio,
|
||||
"response_format": args.response_format,
|
||||
"n": args.n,
|
||||
"prompt_optimizer": args.prompt_optimizer
|
||||
}
|
||||
|
||||
# Add seed if provided
|
||||
if args.seed is not None:
|
||||
payload["seed"] = args.seed
|
||||
|
||||
# Add subject_reference for image-to-image generation
|
||||
if args.subject_reference:
|
||||
subject_ref = build_subject_reference(args.subject_reference, args.subject_type)
|
||||
if subject_ref:
|
||||
payload["subject_reference"] = [subject_ref]
|
||||
else:
|
||||
print("Warning: Failed to process subject reference, continuing without it.")
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"MiniMax Image Generation")
|
||||
print(f"{'='*60}")
|
||||
print(f"Model: {args.model}")
|
||||
print(f"Prompt: {args.prompt}")
|
||||
print(f"Aspect Ratio: {args.aspect_ratio}")
|
||||
print(f"Number: {args.n}")
|
||||
print(f"Prompt Optimizer: {'Enabled' if args.prompt_optimizer else 'Disabled'}")
|
||||
if args.seed is not None:
|
||||
print(f"Seed: {args.seed}")
|
||||
if args.subject_reference:
|
||||
print(f"Subject Reference: {args.subject_type} - {args.subject_reference}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
try:
|
||||
print("Generating images...")
|
||||
response = requests.post(url, headers=headers, json=payload, timeout=60)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
|
||||
if result.get("base_resp", {}).get("status_code") != 0:
|
||||
error_msg = result.get("base_resp", {}).get("status_msg", "Unknown error")
|
||||
print(f"API Error: {error_msg}")
|
||||
return False
|
||||
|
||||
# Create output directory
|
||||
os.makedirs(args.output_dir, exist_ok=True)
|
||||
|
||||
# Process returned images
|
||||
image_urls = result.get("data", {}).get("image_urls", [])
|
||||
metadata = result.get("metadata", {})
|
||||
|
||||
print(f"\nSuccessfully generated {metadata.get('success_count', len(image_urls))} images:")
|
||||
|
||||
timestamp = int(time.time())
|
||||
saved_count = 0
|
||||
|
||||
for i, image_url in enumerate(image_urls, 1):
|
||||
# Extract file extension from URL
|
||||
parsed = urlparse(image_url)
|
||||
path = parsed.path
|
||||
ext = os.path.splitext(path)[1] if "." in path else ".jpeg"
|
||||
|
||||
filename = f"generated_image_{i}_{timestamp}{ext}"
|
||||
output_path = os.path.join(args.output_dir, filename)
|
||||
|
||||
if download_image(image_url, output_path):
|
||||
saved_count += 1
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Done! Successfully saved {saved_count}/{len(image_urls)} images")
|
||||
print(f"Output directory: {os.path.abspath(args.output_dir)}")
|
||||
print(f"{'='*60}\n")
|
||||
|
||||
return saved_count > 0
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
print(f"\nRequest Error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"\nUnexpected Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Main function"""
|
||||
args = parse_args()
|
||||
|
||||
# Get API key from argument or environment variable (optional)
|
||||
if not args.api_key:
|
||||
args.api_key = os.environ.get("MINIMAX_API_KEY", "")
|
||||
|
||||
# If still no API key, prompt user to enter it
|
||||
if not args.api_key:
|
||||
print("Error: API key not int ENV, and it is required.")
|
||||
else:
|
||||
generate_images(args)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user