feat: 增加qwen 生图

This commit is contained in:
2026-04-26 11:41:57 +08:00
parent 52479458d2
commit 61b1de2730
7 changed files with 956 additions and 67 deletions
+176
View File
@@ -0,0 +1,176 @@
---
name: image-generation
description: Generate images using MiniMax API with support for custom prompts, aspect ratios, subject references, 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 image-to-image generation.
## Quick Start (MCP Server)
### Setup
1. Set the environment variable:
```bash
export MINIMAX_API_KEY=your-api-key
```
2. Add to your MCP client config (e.g., Claude Desktop, Cursor, etc.):
```json
{
"mcpServers": {
"minimax-image": {
"command": "python",
"args": ["path/to/minimax_image_mcp.py"]
}
}
}
```
3. Use the `generate_image` tool:
```
generate_image({
prompt: "A beautiful mountain landscape at sunset",
aspect_ratio: "16:9"
})
```
## MCP Tool: generate_image
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `prompt` | string | true | - | Image generation prompt |
| `model` | string | false | `image-01` | Image generation model |
| `aspect_ratio` | string | false | `1:1` | Image aspect ratio |
| `n` | integer | false | `1` | Number of images (1-3) |
| `prompt_optimizer` | boolean | false | `false` | Enable prompt optimizer |
| `subject_reference` | string | false | - | Reference image URL or local file path |
| `subject_type` | string | false | `character` | Subject type (character, product, logo, video_subject, other) |
| `seed` | integer | false | - | Random seed for reproducible generation |
| `output_path` | string | false | - | Local path to save image |
### Aspect Ratio Options
| Ratio | Description |
|-------|-------------|
| `1:1` | Square (default) |
| `16:9` | Landscape |
| `9:16` | Portrait |
| `4:3` | Standard landscape |
| `3:4` | Standard portrait |
### Example Usage
```javascript
// Text-to-Image (t2i) - Pure text generation
{
"prompt": "A man in a white t-shirt, full-body, standing front view, outdoors, fashion photography"
}
// Multiple images with custom aspect ratio
{
"prompt": "A realistic portrait",
"aspect_ratio": "16:9",
"n": 3
}
// With subject reference (image-to-image)
{
"prompt": "Transform to anime style",
"subject_reference": "https://example.com/photo.jpg",
"subject_type": "character"
}
// With seed for reproducibility
{
"prompt": "A forest",
"seed": 12345
}
// Save to local path
{
"prompt": "A sunset over the ocean",
"output_path": "./output/sunset"
}
```
## CLI Usage
```bash
python scripts/run.py --api-key "your-api-key" --prompt "your-prompt" [options]
```
### CLI Arguments
| Argument | Type | Required | Description |
|----------|------|----------|-------------|
| `--api-key` | string | true | MiniMax API key |
| `--prompt` | string | true | Generation prompt |
| `--model` | string | false | Image generation model (default: image-01) |
| `--aspect-ratio` | string | false | Image aspect ratio (default: 1:1) |
| `--response-format` | string | false | Response format (`url` or `base64`) |
| `--n` | integer | false | Number of images (default: 1, max: 3) |
| `--prompt-optimizer` | boolean | false | Enable prompt optimizer (default: false) |
| `--subject-reference` | string | false | Reference image URL or local file path |
| `--subject-type` | string | false | Subject type (default: character) |
| `--seed` | integer | false | Random seed for reproducible generation |
| `--output-dir` | string | false | Output directory (default: ./output) |
## API Reference
- **Endpoint**: `POST https://api.minimaxi.com/v1/image_generation`
- **Auth**: Bearer Token
- **Env Var**: `MINIMAX_API_KEY`
## Examples
### MCP Server Examples
```
generate_image({ prompt: "A sunset over the ocean" })
generate_image({
prompt: "A realistic portrait",
aspect_ratio: "16:9",
n: 3
})
generate_image({
prompt: "Transform into anime style",
subject_reference: "https://example.com/photo.jpg",
subject_type: "character"
})
generate_image({
prompt: "A forest",
seed: 12345
})
```
### CLI Examples
```bash
# Text-to-Image (t2i) - Pure text generation
python scripts/run.py --api-key "sk-xxx" --prompt "A man in a white t-shirt, full-body, fashion photography in 90s documentary style"
# Multiple images with custom aspect ratio
python scripts/run.py --api-key "sk-xxx" --prompt "A portrait" --aspect-ratio "16:9" --n 3
# With subject reference (image-to-image)
python scripts/run.py --api-key "sk-xxx" --prompt "Transform to anime style" --subject-reference "https://example.com/photo.jpg" --subject-type "character"
# With local subject reference
python scripts/run.py --api-key "sk-xxx" --prompt "A beautiful scene" --subject-reference "./my_character.jpg" --subject-type "character"
# With seed for reproducibility
python scripts/run.py --api-key "sk-xxx" --prompt "A forest" --seed 42
# With custom output directory
python scripts/run.py --api-key "sk-xxx" --prompt "A mountain" --output-dir "./my-images"
```
@@ -0,0 +1,198 @@
#!/usr/bin/env python3
"""
MiniMax Image Generation MCP Server
Provides image generation via MiniMax API
"""
import os
import base64
import requests
from urllib.parse import urlparse
from mcp.server.fastmcp import FastMCP
# Initialize MCP server
mcp = FastMCP("minimax-image-generator")
def read_local_image(file_path: str) -> str | None:
"""
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()
return base64.b64encode(image_data).decode("utf-8")
except Exception as e:
return None
def build_subject_reference(subject_ref: str, subject_type: str) -> dict | None:
"""
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 or None if failed
"""
# 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
base64_data = read_local_image(subject_ref)
if base64_data:
return {
"type": subject_type,
"image_file": f"data:image/jpeg;base64,{base64_data}"
}
return None
@mcp.tool()
def generate_image(
prompt: str,
model: str = "image-01",
aspect_ratio: str = "1:1",
n: int = 1,
prompt_optimizer: bool = False,
subject_reference: str | None = None,
subject_type: str = "character",
seed: int | None = None,
output_path: str | None = None
) -> str:
"""
Generate images using MiniMax image generation API.
Args:
prompt: The image generation prompt describing what to generate
model: Image generation model (default: image-01)
aspect_ratio: Image aspect ratio (default: 1:1, options: 1:1, 16:9, 9:16, 4:3, 3:4)
n: Number of images to generate (default: 1, max: 3)
prompt_optimizer: Enable prompt optimizer (default: false)
subject_reference: Reference image URL or local file path for image-to-image generation
subject_type: Subject reference type (default: character, options: character, product, logo, video_subject, other)
seed: Random seed for reproducible generation
output_path: Optional local path to save the generated image
Returns:
Image URLs and generation status
"""
api_key = os.environ.get("MINIMAX_API_KEY", "")
api_base = "https://api.minimaxi.com"
if not api_key:
return "Error: MINIMAX_API_KEY environment variable is not set"
# Build request payload
payload = {
"model": model,
"prompt": prompt,
"aspect_ratio": aspect_ratio,
"response_format": "url",
"n": n,
"prompt_optimizer": prompt_optimizer
}
# Add seed if provided
if seed is not None:
payload["seed"] = seed
# Add subject_reference for image-to-image generation
if subject_reference:
subject_ref = build_subject_reference(subject_reference, subject_type)
if subject_ref:
payload["subject_reference"] = [subject_ref]
# Make API request
url = f"{api_base}/v1/image_generation"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
try:
response = requests.post(url, headers=headers, json=payload, timeout=60)
response.raise_for_status()
result = response.json()
# Check for API errors
if result.get("base_resp", {}).get("status_code") != 0:
error_msg = result.get("base_resp", {}).get("status_msg", "Unknown error")
return f"API Error: {error_msg}"
# Extract image URLs
image_urls = result.get("data", {}).get("image_urls", [])
metadata = result.get("metadata", {})
request_id = result.get("id", "N/A")
success_count = metadata.get("success_count", len(image_urls))
# Save image if output_path provided
saved_paths = []
if output_path and image_urls:
try:
import time
timestamp = int(time.time())
for i, img_url in enumerate(image_urls, 1):
img_response = requests.get(img_url, timeout=30)
img_response.raise_for_status()
# Determine file extension
parsed = urlparse(img_url)
ext = os.path.splitext(parsed.path)[1] if "." in parsed.path else ".jpeg"
if not ext or len(ext) > 5:
ext = ".jpeg"
# Handle multiple images
if len(image_urls) > 1:
base_path = output_path.rsplit('.', 1)[0] if '.' in output_path else output_path
file_ext = output_path.rsplit('.', 1)[1] if '.' in output_path else ext
img_path = f"{base_path}_{i}_{timestamp}.{file_ext}"
else:
if not output_path.endswith(ext):
img_path = output_path + ext
else:
img_path = output_path
# Ensure directory exists
os.makedirs(os.path.dirname(img_path) if os.path.dirname(img_path) else ".", exist_ok=True)
with open(img_path, "wb") as f:
f.write(img_response.content)
saved_paths.append(os.path.abspath(img_path))
except Exception as e:
return f"Failed to save image: {str(e)}"
# Build response
response_text = f"Successfully generated {success_count} image(s)\n\n"
response_text += f"Request ID: {request_id}\n\n"
if saved_paths:
response_text += "Saved to:\n"
for path in saved_paths:
response_text += f" - {path}\n"
response_text += "\n"
response_text += "Image URLs:\n"
for i, img_url in enumerate(image_urls, 1):
response_text += f" {i}. {img_url}\n"
return response_text
except requests.exceptions.RequestException as e:
return f"Request Error: {str(e)}"
except Exception as e:
return f"Unexpected Error: {str(e)}"
if __name__ == "__main__":
mcp.run()
+287
View File
@@ -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()