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
+162
View File
@@ -0,0 +1,162 @@
---
name: qwen-image-generation
description: Generate images using Qwen DashScope API with support for custom prompts, aspect ratios, and multiple image generation.
metadata: {"clawdbot":{"emoji":"🎨","os":["linux","darwin","win32"]}}
---
# Qwen Image Generation SKILL
## Description
Generate images using Qwen (通义千问) API via DashScope. Supports custom prompts, aspect ratios, generation count, prompt enhancement, and image-to-image generation.
## Quick Start (MCP Server)
### Setup
1. Set the environment variable:
```bash
export DASHSCOPE_API_KEY=your-api-key
```
2. Add to your MCP client config (e.g., Claude Desktop, Cursor, etc.):
```json
{
"mcpServers": {
"qwen-image": {
"command": "python",
"args": ["path/to/qwen_image_mcp.py"]
}
}
}
```
3. Use the `generate_image` tool:
```
generate_image({
prompt: "A beautiful mountain landscape at sunset",
size: "1024*1024"
})
```
## MCP Tool: generate_image
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `prompt` | string | true | - | Image generation prompt (max 800 chars) |
| `negative_prompt` | string | false | - | Negative prompt to avoid elements (max 500 chars) |
| `prompt_extend` | boolean | false | `true` | Enable prompt enhancement |
| `size` | string | false | `1024*1024` | Image resolution |
| `n` | integer | false | `1` | Number of images (1-6) |
| `image_url` | string | false | - | Reference image URL for img2img |
| `output_path` | string | false | - | Local path to save image |
### Size Options
| Size | Aspect Ratio |
|------|--------------|
| `1024*1024` | 1:1 (default) |
| `1344*768` | 16:9 |
| `768*1344` | 9:16 |
| `1184*864` | 4:3 |
| `864*1184` | 3:4 |
### Example Usage
```javascript
// Basic generation
{
"prompt": "A beautiful mountain landscape at sunset"
}
// High resolution with multiple images
{
"prompt": "A realistic portrait",
"size": "1024*1024",
"n": 3
}
// With reference image
{
"prompt": "Transform to oil painting style",
"image_url": "https://example.com/input.jpg"
}
// With local save
{
"prompt": "A sunset over the ocean",
"output_path": "./output/sunset"
}
// With negative prompt
{
"prompt": "A beautiful garden",
"negative_prompt": "blurry, low quality, distorted"
}
```
## 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 | DashScope API key |
| `--prompt` | string | true | Generation prompt |
| `--size` | string | false | Image size (default: 1024*1024) |
| `--n` | integer | false | Number of images (default: 1, max: 6) |
| `--negative-prompt` | string | false | Negative prompt |
| `--prompt-extend` | boolean | false | Enable prompt extend (default: true) |
| `--image-url` | string | false | Reference image URL for img2img |
| `--output-path` | string | false | Local path to save image |
## API Reference
- **Endpoint**: `POST https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation`
- **Auth**: Bearer Token
- **Env Var**: `DASHSCOPE_API_KEY`
- **Model**: `qwen-image-2.0-pro`
## Examples
### MCP Server Examples
```
generate_image({ prompt: "A sunset over the ocean" })
generate_image({
prompt: "A realistic portrait",
size: "1024*1024",
n: 3
})
generate_image({
prompt: "Transform into anime style",
image_url: "https://example.com/photo.jpg"
})
```
### CLI Examples
```bash
# Basic generation
python scripts/run.py --api-key "sk-xxx" --prompt "A sunset"
# Multiple images with custom size
python scripts/run.py --api-key "sk-xxx" --prompt "A portrait" --size "1024*1024" --n 3
# With negative prompt
python scripts/run.py --api-key "sk-xxx" --prompt "A garden" --negative-prompt "blurry, low quality"
# With reference image
python scripts/run.py --api-key "sk-xxx" --prompt "Transform to anime style" --image-url "https://example.com/photo.jpg"
# Save to local path
python scripts/run.py --api-key "sk-xxx" --prompt "A mountain" --output-path "./output/mountain"
```
@@ -0,0 +1,161 @@
#!/usr/bin/env python3
"""
Qwen Image Generation MCP Server
Provides image generation via Qwen (DashScope) API
"""
import os
import requests
from urllib.parse import urlparse
from mcp.server.fastmcp import FastMCP
# Initialize MCP server
mcp = FastMCP("qwen-image-generator")
@mcp.tool()
def generate_image(
prompt: str,
negative_prompt: str | None = None,
prompt_extend: bool = True,
size: str = "1024*1024",
n: int = 1,
image_url: str | None = None,
output_path: str | None = None
) -> str:
"""
Generate images using Qwen (通义千问) image generation API.
Args:
prompt: The image generation prompt describing what to generate (max 800 chars)
negative_prompt: Negative prompt to avoid certain elements (max 500 chars)
prompt_extend: Enable prompt extend to enhance the prompt (default: true)
watermark: Add watermark to generated image (default: false)
size: Image resolution. Options: 1024*1024 (1:1), 1344*768 (16:9), 768*1344 (9:16), 1184*864 (4:3), 864*1184 (3:4)
n: Number of images to generate, 1-6 (default: 1)
image_url: Optional reference image URL for image-to-image generation
output_path: Optional local path to save the generated image
Returns:
Image URLs and generation status
"""
api_key = os.environ.get("DASHSCOPE_API_KEY", "")
api_base = "https://dashscope.aliyuncs.com"
if not api_key:
return "Error: DASHSCOPE_API_KEY environment variable is not set"
# Build content array
content = [{"text": prompt}]
# Add reference image if provided
if image_url:
content.append({"image_url": {"url": image_url}})
# Build parameters dict
parameters = {
"prompt_extend": prompt_extend,
"size": size,
"n": n
}
# Only add negative_prompt if provided
if negative_prompt:
parameters["negative_prompt"] = negative_prompt
# Build request payload
payload = {
"model": "qwen-image-2.0-pro",
"input": {
"messages": [
{
"role": "user",
"content": content
}
]
},
"parameters": parameters
}
# Make API request
url = f"{api_base}/api/v1/services/aigc/multimodal-generation/generation"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json"
}
try:
response = requests.post(url, headers=headers, json=payload, timeout=180)
response.raise_for_status()
result = response.json()
# Check for API errors
if "error" in result:
error_msg = result.get("error", {}).get("message", "Unknown error")
return f"API Error: {error_msg}"
# Parse response
choices = result.get("output", {}).get("choices", [])
usage = result.get("usage", {})
# Extract image URLs
image_urls = []
for choice in choices:
message = choice.get("message", {})
content_items = message.get("content", [])
for item in content_items:
if "image" in item:
image_urls.append(item["image"])
width = usage.get("width", 1024)
height = usage.get("height", 1024)
request_id = result.get("request_id", "N/A")
# Save image if output_path provided
saved_path = None
if output_path and image_urls:
try:
img_response = requests.get(image_urls[0], timeout=30)
img_response.raise_for_status()
# Determine file extension
parsed = urlparse(image_urls[0])
ext = os.path.splitext(parsed.path)[1] if "." in parsed.path else ".png"
if not ext or len(ext) > 5:
ext = ".png"
# Ensure directory exists
os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else ".", exist_ok=True)
# Add extension if not present
if not output_path.endswith(ext):
output_path = output_path + ext
with open(output_path, "wb") as f:
f.write(img_response.content)
saved_path = os.path.abspath(output_path)
except Exception as e:
return f"Failed to save image: {str(e)}"
# Build response
response_text = f"Successfully generated {n} image(s) ({width}x{height})\n\n"
response_text += f"Request ID: {request_id}\n\n"
if saved_path:
response_text += f"Saved to: {saved_path}\n\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()
+259
View File
@@ -0,0 +1,259 @@
#!/usr/bin/env python3
# @skill: qwen-image-generation
"""
Qwen Image Generation Script
Generate images using Qwen (DashScope) 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 Qwen (DashScope) API",
formatter_class=argparse.RawDescriptionHelpFormatter
)
parser.add_argument(
"--api-key",
type=str,
required=True,
help="DashScope API key (can also be set via DASHSCOPE_API_KEY environment variable)"
)
parser.add_argument(
"--prompt",
type=str,
required=True,
help="Image generation prompt (max 800 chars)"
)
parser.add_argument(
"--size",
type=str,
default="1024*1024",
help="Image resolution (default: 1024*1024, options: 1344*768, 768*1344, 1184*864, 864*1184)"
)
parser.add_argument(
"--n",
type=int,
default=1,
choices=range(1, 7),
help="Number of images to generate (default: 1, max: 6)"
)
parser.add_argument(
"--negative-prompt",
type=str,
default=None,
help="Negative prompt to avoid certain elements (max 500 chars)"
)
parser.add_argument(
"--prompt-extend",
type=lambda x: x.lower() == "true",
default=True,
help="Enable prompt extend to enhance the prompt (default: true)"
)
parser.add_argument(
"--image-url",
type=str,
default=None,
help="Reference image URL for image-to-image generation"
)
parser.add_argument(
"--output-path",
type=str,
default=None,
help="Local path to save the generated image"
)
parser.add_argument(
"--api-base",
type=str,
default="https://dashscope.aliyuncs.com",
help="API base URL (default: https://dashscope.aliyuncs.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 generate_images(args):
"""Call Qwen (DashScope) API to generate images"""
url = f"{args.api_base}/api/v1/services/aigc/multimodal-generation/generation"
headers = {
"Authorization": f"Bearer {args.api_key}",
"Content-Type": "application/json"
}
# Build content array
content = [{"text": args.prompt}]
# Add reference image if provided
if args.image_url:
content.append({"image_url": {"url": args.image_url}})
# Build parameters dict
parameters = {
"prompt_extend": args.prompt_extend,
"size": args.size,
"n": args.n
}
# Only add negative_prompt if provided
if args.negative_prompt:
parameters["negative_prompt"] = args.negative_prompt
# Build request payload
payload = {
"model": "qwen-image-2.0-pro",
"input": {
"messages": [
{
"role": "user",
"content": content
}
]
},
"parameters": parameters
}
print(f"\n{'='*60}")
print(f"Qwen Image Generation")
print(f"{'='*60}")
print(f"Model: qwen-image-2.0-pro")
print(f"Prompt: {args.prompt}")
print(f"Size: {args.size}")
print(f"Number: {args.n}")
print(f"Prompt Extend: {'Enabled' if args.prompt_extend else 'Disabled'}")
if args.negative_prompt:
print(f"Negative Prompt: {args.negative_prompt}")
if args.image_url:
print(f"Reference Image: {args.image_url}")
print(f"{'='*60}\n")
try:
print("Generating images...")
response = requests.post(url, headers=headers, json=payload, timeout=180)
response.raise_for_status()
result = response.json()
# Check for API errors
if "error" in result:
error_msg = result.get("error", {}).get("message", "Unknown error")
print(f"API Error: {error_msg}")
return False
# Parse response
choices = result.get("output", {}).get("choices", [])
usage = result.get("usage", {})
# Extract image URLs
image_urls = []
for choice in choices:
message = choice.get("message", {})
content_items = message.get("content", [])
for item in content_items:
if "image" in item:
image_urls.append(item["image"])
width = usage.get("width", 1024)
height = usage.get("height", 1024)
request_id = result.get("request_id", "N/A")
print(f"\nSuccessfully generated {len(image_urls)} image(s) ({width}x{height})")
print(f"Request ID: {request_id}\n")
saved_count = 0
# If output_path is provided, save all images
if args.output_path:
timestamp = int(time.time())
for i, img_url in enumerate(image_urls, 1):
# Determine file extension from URL
parsed = urlparse(img_url)
ext = os.path.splitext(parsed.path)[1] if "." in parsed.path else ".png"
if not ext or len(ext) > 5:
ext = ".png"
# Handle multiple images
if len(image_urls) > 1:
base_path = args.output_path.rsplit('.', 1)[0] if '.' in args.output_path else args.output_path
ext = args.output_path.rsplit('.', 1)[1] if '.' in args.output_path else ext
output_path = f"{base_path}_{i}_{timestamp}.{ext}"
else:
if not args.output_path.endswith(ext):
output_path = f"{args.output_path}{ext}"
else:
output_path = args.output_path
# Ensure directory exists
os.makedirs(os.path.dirname(output_path) if os.path.dirname(output_path) else ".", exist_ok=True)
if download_image(img_url, output_path):
saved_count += 1
else:
# Print URLs
print("Image URLs:")
for i, img_url in enumerate(image_urls, 1):
print(f" {i}. {img_url}")
print(f"\n{'='*60}")
if args.output_path:
print(f"Done! Successfully saved {saved_count}/{len(image_urls)} images")
print(f"{'='*60}\n")
return saved_count > 0 or (len(image_urls) > 0 and not args.output_path)
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
if not args.api_key:
args.api_key = os.environ.get("DASHSCOPE_API_KEY", "")
# If still no API key, prompt user to enter it
if not args.api_key:
print("Error: API key is required (--api-key or DASHSCOPE_API_KEY)")
else:
generate_images(args)
if __name__ == "__main__":
main()