{"repo": "aliyun/alibabacloud-aiops-skills", "files": {"skills/aiml/sfm/alibabacloud-bailian-image-creator/SKILL.md": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674219.3540306, "n": 20306, "h": "cc57a90f08fe6ad2", "c": "---\nname: alibabacloud-bailian-image-creator\ndescription: AI image creation skill supporting text-to-image, image editing, image understanding, and more. Uses qwen-image-2.0-pro, wan2.7-image, qwen3.5-plus and other models. Use this skill when users need to generate images, edit images, analyze image content, or perform image-related tasks. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Image Creator\n\nProfessional-grade AI image creation skill built on Alibaba Cloud DashScope API.\n\n> **Warning: Before executing any image task, you must read and follow the \"Mandatory Rules\" section. Violations will cause task failure.**\n\n## Mandatory Rules\n\n### First Principle: Must Use Existing Scripts\n\n**All image generation, editing, and understanding tasks must and can only be completed by running existing scripts in the `scripts/` directory.**\n\n- **Do NOT write your own API call code** -- Do not create new Python scripts to call DashScope API. You must use the scripts specified in the \"Task-to-Script Mapping\" table below\n- **Do NOT use PIL/Pillow or other local libraries** for image generation or editing (only allowed for auxiliary operations such as size validation)\n- **Do NOT use third-party APIs** -- Including but not limited to Pollinations.ai, Stability.ai, DALL-E, Midjourney\n- **Do NOT create mock/simulated scripts** -- Do not bypass real API calls in any way\n- **Do NOT use deprecated models** -- `wanx-v1`, `wanx-v2`, `wan2.6-image` and other legacy models are discontinued\n- **Do NOT mix APIs** -- Qwen models (qwen-*) can only use `MultiModalConversation.call()`, Wanx models (wan2.7-*) can only use `ImageGeneration.call()`\n- **Do NOT use curl to call DashScope REST API** -- Must call through provided scripts, do not write shell scripts as substitutes\n- **Do NOT generate placeholder/blank images** -- Do not use hardcoded byte streams, blank canvases, or PIL-scaled images to impersonate API-generated images\n- **Do NOT claim success when API calls fail** -- Must truthfully report failure reasons, do not generate false success reports\n\n### Task-to-Script Mapping (The Only Execution Method)\n\nBased on user request keywords, look up the table to select the script. **No other methods are allowed**:\n\n| User Request Keywords | Script | API | Default Model |\n|----------------------|--------|-----|---------------|\n| Wanx, wan2.7, reference image + generation, style fusion, multi-image fusion, graffiti-on-car, virtual try-on | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image` |\n| 4K HD text-to-image | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image-pro` |\n| A set of coherent images, image series generation | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image-pro` |\n| Text-to-image, generate image, draw a picture (no reference image) | `text_to_image.py` | `MultiModalConversation.call()` | `qwen-image-2.0-pro` |\n| Edit image (URL input) | `image_edit.py` | `MultiModalConversation.call()` | `qwen-image-edit-max` |\n| Edit image (local file) | `image_edit_base64.py` | `MultiModalConversation.call()` | `qwen-image-edit-max` |\n| Analyze/understand image content, describe image | `image_understanding.py` | `chat.completions.create()` | `qwen3.5-plus` |\n\n**Disambiguation Priority Rules** (when multiple keywords match simultaneously):\n1. User mentions \"Wanx\" or \"wan2.7\" -> Use `wanx_generate.py` directly, regardless of whether \"generate image\" or similar words are present\n2. User provides **reference image URLs** and requests generating a new image -> Use `wanx_generate.py` (Wanx supports multi-image reference input)\n3. Text description only, no reference image -> Use `text_to_image.py`\n\n**Allowed Models**:\n\n| Script | Allowed Models |\n|--------|---------------|\n| `text_to_image.py` | `qwen-image-2.0-pro`, `qwen-image-2.0` |\n| `wanx_generate.py` | `wan2.7-image-pro`, `wan2.7-image` |\n| `image_edit.py` / `image_edit_base64.py` | `qwen-image-edit-max`, `qwen-image-2.0-pro`, `qwen-image-edit-plus`, `qwen-image-edit` |\n| `image_understanding.py` | `qwen3.5-plus`, `qwen-vl-max`, `qwen-vl-plus` |\n\n### API Key Security Management\n\n**Scripts automatically handle key retrieval via `api_key.py`. The Agent does not need to and should not manually extract, set, or pass API Key values.**\n\n1. **Key retrieval is automated**: Scripts internally call `api_key.py` to automatically obtain keys from config files/environment variables, or auto-create via CLI. The Agent only needs to run the script command\n2. **Never hardcode any form of key**: Including `api_key = \"sk-...\"`, `export DASHSCOPE_API_KEY=\"sk-...\"`, and assigning keys in shell scripts\n3. **Never extract keys from CLI output**: The key value returned by `aliyun modelstudio create-api-key` is automatically saved by `api_key.py`. The Agent must not write this value into any script, variable, or file\n4. **Never expose keys in any output**: Including generated scripts, shell commands, log files (summary.md, task_summary.md, execution_log.md, etc.), and terminal output containing strings starting with `sk-`\n5. **Never read or print keys from config files**: Do not use `cat ~/.aliyun/config.json`, `jq`, `python -c`, or other commands to read and output API Key values from config files\n6. **Never directly call CLI key commands**: Do not directly run `aliyun modelstudio create-api-key` or `aliyun modelstudio list-api-keys`. These commands are only called internally by `api_key.py`\n7. **Mandatory self-check before task completion**: Run `grep -rn \"sk-\" /` to check all output files; if any strings starting with `sk-` are found (excluding \"sk-xxx\" placeholders), delete the affected files and regenerate\n\n### API Call Result Validation\n\n1. **Check status code**: API response `status_code` must be `200`, otherwise abort the task\n2. **Check output content**: Response must contain valid image URLs (starting with `http`). No valid URL means failure\n3. **Never fabricate results**: Do not generate simulated data, false success reports, or placeholder image URLs\n4. **Never mock/simulate**: Do not create mock scripts or simulate API calls to bypass real calls\n5. **Correct behavior on failure**:\n - Print error information (status code, error code, error message)\n - Stop executing subsequent steps\n - If it's a key issue, `api_key.py` will automatically retry creation\n - If retries still fail, report the real error reason without concealment\n\n### Output Validation (Must execute before reporting task completion)\n\n1. **Check output files**: Confirm image files exist in the output directory and have reasonable size (typically > 10KB)\n2. **Key leak scan**: Run `grep -rn \"sk-\" /` to check all output files (including logs, summaries, scripts). If leaks are found, delete and regenerate\n3. **No false claims**: If API calls actually failed or were not executed, do not claim success in the task summary (e.g., listing checkmarks)\n4. **Blank image detection**: If output images are blank, solid color, or merely scaled versions of input images, treat as task failure\n\n### Output Size Control\n\n| Model Family | Size Format | Supported Values |\n|-------------|-------------|------------------|\n| `qwen-image-2.0-pro` / `qwen-image-2.0` (text-to-image) | `\"width*height\"` | `1024*1024`, `720*1280`, `1280*720`, `1440*720`, `720*1440` |\n| `qwen-image-edit-*` / `qwen-image-2.0-pro` (editing) | `\"width*height\"` | Width and height each [512, 2048], freely combined |\n| `wan2.7-image-pro` (text-to-image) | Preset tier | `1K`, `2K`, `4K` |\n| `wan2.7-image-pro` (editing/series) | Preset tier | `1K`, `2K` |\n| `wan2.7-image` | Preset tier | `1K`, `2K` |\n\n**Mapping Rules**:\n1. User requests exact pixels (e.g., `1280x720`) -> Choose Qwen series, set `size='1280*720'`\n2. User requests portrait/landscape -> Use the closest fixed size for Qwen text-to-image (e.g., portrait -> `720*1280`)\n3. Wanx models -> `size` can only be `1K`/`2K`/`4K`, not pixel values\n\n---\n\n## Execution Flow\n\n### Step 1: Install Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n### Step 2: Select and Run Script Based on \"Task-to-Script Mapping\" Table\n\n**Selection Logic**: First check if the user mentions \"Wanx\"/\"wan2.7\" or provides reference image URLs -> If yes, use `wanx_generate.py`; otherwise match by other keywords in the mapping table.\n\nAPI Key is automatically managed by scripts (three-level retrieval: config file -> environment variable -> auto-create). **No manual key handling is needed**. The Agent only needs to run the commands in the table below. Do not write your own scripts, set environment variables, or use curl to call APIs.\n\n| Scenario | Command | Example |\n|----------|---------|---------|\n| Text-to-image (Qwen) | `python scripts/text_to_image.py [size] [model]` | `python scripts/text_to_image.py 'An orange cat sitting on a windowsill, 8K' 1024*1024 qwen-image-2.0-pro` |\n| Text-to-image / Multi-image fusion (Wanx) | `python scripts/wanx_generate.py [ref_image_URLs...]` | `python scripts/wanx_generate.py 'Paint the graffiti from image 2 on the car in image 1' url1 url2` |\n| Image editing (URL input) | `python scripts/image_edit.py [URL2] [URL3]` | `python scripts/image_edit.py 'Change the background to white' https://example.com/photo.png` |\n| Image editing (local file) | `python scripts/image_edit_base64.py [image2] [image3]` | `python scripts/image_edit_base64.py 'Change the background to white' ./photo.png` |\n| Image understanding | `python scripts/image_understanding.py [question]` | `python scripts/image_understanding.py https://example.com/photo.jpg 'What is in this image?'` |\n\n### Step 3: Return Results\n\nScripts will print the list of generated image URLs; `text_to_image.py` also auto-downloads images to the current directory.\n\n---\n\n## Model Selection Guide\n\n| Condition | Choice | Reason |\n|-----------|--------|--------|\n| Need accurate in-image text rendering | `qwen-image-2.0-pro` | Qwen series has the strongest text rendering capability |\n| Need 4K ultra-high definition | `wan2.7-image-pro` | Only Wanx pro text-to-image supports 4K |\n| Need a set of style-consistent images | `wan2.7-image-pro` + `enable_sequential` | Wanx exclusive image series capability |\n| Product/industrial design editing | `qwen-image-edit-max` | Stronger geometric reasoning and character consistency |\n| Multi-image reference + composition | `wan2.7-image` | Wanx supports flexible multi-image reference editing |\n| Image content analysis/Q&A | `qwen3.5-plus` | General multimodal understanding, cost-effective |\n| Complex visual reasoning | `qwen-vl-max` | Stronger visual understanding capability |\n| Low cost priority | `qwen-image-2.0` / `wan2.7-image` | Standard version at lower cost |\n| No special requirements (default) | `qwen-image-2.0-pro` | Most balanced overall capability |\n\n## Prompt Design Guide\n\n> For detailed guide, see [Prompt Design Guide](references/prompt-guide.md)\n\nPrompt structure formula: `[Subject description] + [Scene/environment] + [Lighting/mood] + [Composition/angle] + [Art style] + [Quality parameters]`\n\n**Complete example**:\n```\nA 30-year-old Asian male detective standing on a rainy night Tokyo street, wearing a dark gray trench coat, holding a black umbrella, neon red and blue lights reflecting on his face, raindrops dripping along the umbrella edge, puddles on the ground reflecting brilliant city lights, Rembrandt lighting, volumetric fog, cinematic composition, 35mm anamorphic lens, film grain, moody atmosphere, high detail, photorealistic, 8K\n```\n\n**Negative prompt template** (Qwen series supports `negative_prompt`, Wanx series does not):\n```\nlow quality, blurry, pixelated, oversaturated, deformed hands, extra fingers, distorted face, uncanny valley, text, watermark, logo, signature, cropped, out of frame, worst quality\n```\n\n---\n\n## Reference Information\n\n### Environment & Dependencies\n\n| Package | Version Constraint | Purpose |\n|---------|-------------------|---------|\n| `dashscope` | `==1.25.16` | Text-to-image, image editing, Wanx generation |\n| `openai` | `==2.23.0` | Image understanding (OpenAI-compatible interface) |\n\n### API Key Configuration\n\nThis skill manages DashScope API Keys through `scripts/api_key.py`, which automatically validates key format (detects invalid keys like `sk-sp-`).\n\n**Retrieval Priority**: `~/.aliyun/config.json` > Environment variable `DASHSCOPE_API_KEY` > Auto-create via Alibaba Cloud CLI\n\n**Method 1: Alibaba Cloud CLI Config File (Recommended)**\n\nAdd a `dashscope` field to the current profile in `~/.aliyun/config.json`:\n```json\n{\n \"current\": \"default\",\n \"profiles\": [\n {\n \"name\": \"default\",\n \"mode\": \"AK\",\n \"access_key_id\": \"...\",\n \"access_key_secret\": \"...\",\n \"dashscope\": {\n \"api_key\": \"sk-xxx\",\n \"api_key_id\": \"4359606\"\n }\n }\n ]\n}\n```\n\n**Method 2: System Environment Variable**\n\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support image generation) |\n| **Get Key** | https://help.aliyun.com/zh/model-studio/get-api-key |\n| **Security** | Never hardcode; use `from api_key import get_api_key`; **never output full API Key values to terminal, logs, or any files** |\n\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\n`scripts/api_key.py` creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n\n# Install ModelStudio plugin (if not already installed)\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n\n# Disable AI-Mode after task completion\naliyun configure ai-mode disable\n```\n\n| Command | Purpose | Called From |\n|---------|---------|------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio list-api-keys` | List existing API Keys (used for limit recycling) | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: delete_api_key()` / `generate_api_key()` |\n\n### Regions & API Endpoints\n\n| Region | DashScope API | OpenAI Compatible Mode |\n|--------|---------------|------------------------|\n| Beijing (China) | `https://dashscope.aliyuncs.com/api/v1` | `https://dashscope.aliyuncs.com/compatible-mode/v1` |\n| Singapore | `https://dashscope-intl.aliyuncs.com/api/v1` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` |\n| Virginia (US) | `https://dashscope-us.aliyuncs.com/api/v1` | `https://dashscope-us.aliyuncs.com/compatible-mode/v1` |\n\n**Note**: API Keys are not interchangeable between regions.\n\n### Script Parameter Reference\n\n#### text_to_image.py -- Text-to-Image (Qwen Series)\n\n| Parameter | Description |\n|-----------|-------------|\n| `size` | Only supports `1024*1024`, `720*1280`, `1280*720`, `1440*720`, `720*1440` |\n| `prompt_extend` | True to auto-optimize prompts |\n| `negative_prompt` | Negative prompt to avoid generating undesired content |\n| `watermark` | Whether to add watermark |\n\n#### image_edit.py / image_edit_base64.py -- Image Editing (Qwen Series)\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `n` | int | Number of output images, 1-6 (`qwen-image-edit` fixed at 1) |\n| `size` | str | Output resolution, format `\"width*height\"`, range [512, 2048] |\n| `negative_prompt` | str | Negative prompt |\n| `prompt_extend` | bool | Smart prompt rewriting, default true |\n| `watermark` | bool | Whether to add watermark, default false |\n| `seed` | int | Random seed [0, 2147483647], for result reproduction |\n\n**Editing Model Comparison**:\n\n| Model | Strengths | Use Case |\n|-------|-----------|----------|\n| `qwen-image-edit-max` | Strong geometric reasoning, character consistency | Product design, precise editing |\n| `qwen-image-2.0-pro` | Strong text rendering, realistic quality | General editing, text editing |\n| `qwen-image-edit-plus` | Multi-image output and custom resolution | General editing |\n| `qwen-image-edit` | Basic version, fixed single image output | Simple editing |\n\n**Common Resolution Recommendations**:\n\n| Aspect Ratio | Recommended Resolution |\n|-------------|----------------------|\n| 1:1 | `1024*1024`, `1536*1536` |\n| 2:3 | `768*1152`, `1024*1536` |\n| 3:2 | `1152*768`, `1536*1024` |\n| 3:4 | `960*1280`, `1080*1440` |\n| 4:3 | `1280*960`, `1440*1080` |\n| 9:16 | `720*1280`, `1080*1920` |\n| 16:9 | `1280*720`, `1920*1080` |\n| 21:9 | `1344*576`, `2048*872` |\n\n**Image Input Methods**:\n- **HTTP/HTTPS URL**: `\"https://example.com/image.png\"`\n- **Base64 Encoding**: `\"data:image/png;base64,...\"` -- See `scripts/image_edit_base64.py`\n- Input: 1-3 reference images; Output: 1-6 images (controlled via `n` parameter)\n\n#### wanx_generate.py -- Wanx Image Generation\n\n| Parameter | Description |\n|-----------|-------------|\n| `size` | `1K`, `2K`, `4K` (4K only for `wan2.7-image-pro` text-to-image) |\n| `enable_sequential` | Series mode, generates 1-12 style-consistent images |\n| `thinking_mode` | Default true, enhances reasoning quality (increases latency) |\n| `n` | Normal mode 1-4, series mode 1-12 |\n\n| Model | Strengths |\n|-------|-----------|\n| `wan2.7-image-pro` | Pro version, text-to-image supports 4K HD |\n| `wan2.7-image` | Standard version, faster generation |\n\n#### image_understanding.py -- Image Understanding\n\nUses OpenAI-compatible mode to call DashScope. Default model `qwen3.5-plus` (general understanding), optional `qwen-vl-max` (complex reasoning).\n\n### Pricing\n\n| Model | Unit Price | Notes |\n|-------|-----------|-------|\n| qwen-image-2.0-pro | CN price | Pro version, strongest text rendering and realistic quality |\n| qwen-image-edit-max | CN price | Pro version, image generation and editing fusion |\n| qwen-image-2.0 | CN price | Standard version |\n| qwen-image-edit-plus | CN price | Standard editing |\n| wan2.7-image-pro | CN price | Wanx pro version |\n| wan2.7-image | CN price | Wanx standard version |\n\nNew users get 100 free image credits (valid for 90 days) after activating Bailian. View bills: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Best Practices\n\n1. Use `prompt_extend=True` to let the model auto-optimize prompts\n2. Qwen series supports `negative_prompt`, Wanx series does not support this parameter\n3. Choose appropriate sizes based on requirements to avoid unnecessary large image generation\n4. Use the `n` parameter to generate multiple images at once for selection\n5. Use URLs for online images, Base64 for small local files, `file://` protocol for large files\n\n### Error Handling\n\nScripts have built-in error handling logic. For errors, refer to: https://help.aliyun.com/zh/model-studio/developer-reference/error-code\n\n### Related Resources\n\n- [Prompt Design Guide](references/prompt-guide.md) - Professional prompt construction methods\n- [API Documentation](references/api-docs.md) - Detailed API parameter reference\n- [Model List](references/models.md) - Supported models and features\n- [Error Code Reference](references/error-codes.md) - Common error codes and solutions\n- [Example Scripts](scripts/) - Ready-to-use scripts\n\n| Script | Function | Description |\n|--------|----------|-------------|\n| `text_to_image.py` | Text-to-image | Generate images from text descriptions, supports auto-download |\n| `image_edit.py` | Image editing | Supports URL, local file, and Base64 input methods |\n| `image_edit_base64.py` | Base64 image editing | Demonstrates Base64 encoding method |\n| `base64_tool.py` | Base64 tool | CLI tool for image-Base64 conversion |\n| `wanx_generate.py` | Wanx generation | Uses wan2.7-image model for generation |\n| `image_understanding.py` | Image understanding | Analyze image content, answer questions |\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846853.498112, "n": 20306, "h": "cc57a90f08fe6ad2", "c": "---\nname: alibabacloud-bailian-image-creator\ndescription: AI image creation skill supporting text-to-image, image editing, image understanding, and more. Uses qwen-image-2.0-pro, wan2.7-image, qwen3.5-plus and other models. Use this skill when users need to generate images, edit images, analyze image content, or perform image-related tasks. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Image Creator\n\nProfessional-grade AI image creation skill built on Alibaba Cloud DashScope API.\n\n> **Warning: Before executing any image task, you must read and follow the \"Mandatory Rules\" section. Violations will cause task failure.**\n\n## Mandatory Rules\n\n### First Principle: Must Use Existing Scripts\n\n**All image generation, editing, and understanding tasks must and can only be completed by running existing scripts in the `scripts/` directory.**\n\n- **Do NOT write your own API call code** -- Do not create new Python scripts to call DashScope API. You must use the scripts specified in the \"Task-to-Script Mapping\" table below\n- **Do NOT use PIL/Pillow or other local libraries** for image generation or editing (only allowed for auxiliary operations such as size validation)\n- **Do NOT use third-party APIs** -- Including but not limited to Pollinations.ai, Stability.ai, DALL-E, Midjourney\n- **Do NOT create mock/simulated scripts** -- Do not bypass real API calls in any way\n- **Do NOT use deprecated models** -- `wanx-v1`, `wanx-v2`, `wan2.6-image` and other legacy models are discontinued\n- **Do NOT mix APIs** -- Qwen models (qwen-*) can only use `MultiModalConversation.call()`, Wanx models (wan2.7-*) can only use `ImageGeneration.call()`\n- **Do NOT use curl to call DashScope REST API** -- Must call through provided scripts, do not write shell scripts as substitutes\n- **Do NOT generate placeholder/blank images** -- Do not use hardcoded byte streams, blank canvases, or PIL-scaled images to impersonate API-generated images\n- **Do NOT claim success when API calls fail** -- Must truthfully report failure reasons, do not generate false success reports\n\n### Task-to-Script Mapping (The Only Execution Method)\n\nBased on user request keywords, look up the table to select the script. **No other methods are allowed**:\n\n| User Request Keywords | Script | API | Default Model |\n|----------------------|--------|-----|---------------|\n| Wanx, wan2.7, reference image + generation, style fusion, multi-image fusion, graffiti-on-car, virtual try-on | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image` |\n| 4K HD text-to-image | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image-pro` |\n| A set of coherent images, image series generation | `wanx_generate.py` | `ImageGeneration.call()` | `wan2.7-image-pro` |\n| Text-to-image, generate image, draw a picture (no reference image) | `text_to_image.py` | `MultiModalConversation.call()` | `qwen-image-2.0-pro` |\n| Edit image (URL input) | `image_edit.py` | `MultiModalConversation.call()` | `qwen-image-edit-max` |\n| Edit image (local file) | `image_edit_base64.py` | `MultiModalConversation.call()` | `qwen-image-edit-max` |\n| Analyze/understand image content, describe image | `image_understanding.py` | `chat.completions.create()` | `qwen3.5-plus` |\n\n**Disambiguation Priority Rules** (when multiple keywords match simultaneously):\n1. User mentions \"Wanx\" or \"wan2.7\" -> Use `wanx_generate.py` directly, regardless of whether \"generate image\" or similar words are present\n2. User provides **reference image URLs** and requests generating a new image -> Use `wanx_generate.py` (Wanx supports multi-image reference input)\n3. Text description only, no reference image -> Use `text_to_image.py`\n\n**Allowed Models**:\n\n| Script | Allowed Models |\n|--------|---------------|\n| `text_to_image.py` | `qwen-image-2.0-pro`, `qwen-image-2.0` |\n| `wanx_generate.py` | `wan2.7-image-pro`, `wan2.7-image` |\n| `image_edit.py` / `image_edit_base64.py` | `qwen-image-edit-max`, `qwen-image-2.0-pro`, `qwen-image-edit-plus`, `qwen-image-edit` |\n| `image_understanding.py` | `qwen3.5-plus`, `qwen-vl-max`, `qwen-vl-plus` |\n\n### API Key Security Management\n\n**Scripts automatically handle key retrieval via `api_key.py`. The Agent does not need to and should not manually extract, set, or pass API Key values.**\n\n1. **Key retrieval is automated**: Scripts internally call `api_key.py` to automatically obtain keys from config files/environment variables, or auto-create via CLI. The Agent only needs to run the script command\n2. **Never hardcode any form of key**: Including `api_key = \"sk-...\"`, `export DASHSCOPE_API_KEY=\"sk-...\"`, and assigning keys in shell scripts\n3. **Never extract keys from CLI output**: The key value returned by `aliyun modelstudio create-api-key` is automatically saved by `api_key.py`. The Agent must not write this value into any script, variable, or file\n4. **Never expose keys in any output**: Including generated scripts, shell commands, log files (summary.md, task_summary.md, execution_log.md, etc.), and terminal output containing strings starting with `sk-`\n5. **Never read or print keys from config files**: Do not use `cat ~/.aliyun/config.json`, `jq`, `python -c`, or other commands to read and output API Key values from config files\n6. **Never directly call CLI key commands**: Do not directly run `aliyun modelstudio create-api-key` or `aliyun modelstudio list-api-keys`. These commands are only called internally by `api_key.py`\n7. **Mandatory self-check before task completion**: Run `grep -rn \"sk-\" /` to check all output files; if any strings starting with `sk-` are found (excluding \"sk-xxx\" placeholders), delete the affected files and regenerate\n\n### API Call Result Validation\n\n1. **Check status code**: API response `status_code` must be `200`, otherwise abort the task\n2. **Check output content**: Response must contain valid image URLs (starting with `http`). No valid URL means failure\n3. **Never fabricate results**: Do not generate simulated data, false success reports, or placeholder image URLs\n4. **Never mock/simulate**: Do not create mock scripts or simulate API calls to bypass real calls\n5. **Correct behavior on failure**:\n - Print error information (status code, error code, error message)\n - Stop executing subsequent steps\n - If it's a key issue, `api_key.py` will automatically retry creation\n - If retries still fail, report the real error reason without concealment\n\n### Output Validation (Must execute before reporting task completion)\n\n1. **Check output files**: Confirm image files exist in the output directory and have reasonable size (typically > 10KB)\n2. **Key leak scan**: Run `grep -rn \"sk-\" /` to check all output files (including logs, summaries, scripts). If leaks are found, delete and regenerate\n3. **No false claims**: If API calls actually failed or were not executed, do not claim success in the task summary (e.g., listing checkmarks)\n4. **Blank image detection**: If output images are blank, solid color, or merely scaled versions of input images, treat as task failure\n\n### Output Size Control\n\n| Model Family | Size Format | Supported Values |\n|-------------|-------------|------------------|\n| `qwen-image-2.0-pro` / `qwen-image-2.0` (text-to-image) | `\"width*height\"` | `1024*1024`, `720*1280`, `1280*720`, `1440*720`, `720*1440` |\n| `qwen-image-edit-*` / `qwen-image-2.0-pro` (editing) | `\"width*height\"` | Width and height each [512, 2048], freely combined |\n| `wan2.7-image-pro` (text-to-image) | Preset tier | `1K`, `2K`, `4K` |\n| `wan2.7-image-pro` (editing/series) | Preset tier | `1K`, `2K` |\n| `wan2.7-image` | Preset tier | `1K`, `2K` |\n\n**Mapping Rules**:\n1. User requests exact pixels (e.g., `1280x720`) -> Choose Qwen series, set `size='1280*720'`\n2. User requests portrait/landscape -> Use the closest fixed size for Qwen text-to-image (e.g., portrait -> `720*1280`)\n3. Wanx models -> `size` can only be `1K`/`2K`/`4K`, not pixel values\n\n---\n\n## Execution Flow\n\n### Step 1: Install Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n### Step 2: Select and Run Script Based on \"Task-to-Script Mapping\" Table\n\n**Selection Logic**: First check if the user mentions \"Wanx\"/\"wan2.7\" or provides reference image URLs -> If yes, use `wanx_generate.py`; otherwise match by other keywords in the mapping table.\n\nAPI Key is automatically managed by scripts (three-level retrieval: config file -> environment variable -> auto-create). **No manual key handling is needed**. The Agent only needs to run the commands in the table below. Do not write your own scripts, set environment variables, or use curl to call APIs.\n\n| Scenario | Command | Example |\n|----------|---------|---------|\n| Text-to-image (Qwen) | `python scripts/text_to_image.py [size] [model]` | `python scripts/text_to_image.py 'An orange cat sitting on a windowsill, 8K' 1024*1024 qwen-image-2.0-pro` |\n| Text-to-image / Multi-image fusion (Wanx) | `python scripts/wanx_generate.py [ref_image_URLs...]` | `python scripts/wanx_generate.py 'Paint the graffiti from image 2 on the car in image 1' url1 url2` |\n| Image editing (URL input) | `python scripts/image_edit.py [URL2] [URL3]` | `python scripts/image_edit.py 'Change the background to white' https://example.com/photo.png` |\n| Image editing (local file) | `python scripts/image_edit_base64.py [image2] [image3]` | `python scripts/image_edit_base64.py 'Change the background to white' ./photo.png` |\n| Image understanding | `python scripts/image_understanding.py [question]` | `python scripts/image_understanding.py https://example.com/photo.jpg 'What is in this image?'` |\n\n### Step 3: Return Results\n\nScripts will print the list of generated image URLs; `text_to_image.py` also auto-downloads images to the current directory.\n\n---\n\n## Model Selection Guide\n\n| Condition | Choice | Reason |\n|-----------|--------|--------|\n| Need accurate in-image text rendering | `qwen-image-2.0-pro` | Qwen series has the strongest text rendering capability |\n| Need 4K ultra-high definition | `wan2.7-image-pro` | Only Wanx pro text-to-image supports 4K |\n| Need a set of style-consistent images | `wan2.7-image-pro` + `enable_sequential` | Wanx exclusive image series capability |\n| Product/industrial design editing | `qwen-image-edit-max` | Stronger geometric reasoning and character consistency |\n| Multi-image reference + composition | `wan2.7-image` | Wanx supports flexible multi-image reference editing |\n| Image content analysis/Q&A | `qwen3.5-plus` | General multimodal understanding, cost-effective |\n| Complex visual reasoning | `qwen-vl-max` | Stronger visual understanding capability |\n| Low cost priority | `qwen-image-2.0` / `wan2.7-image` | Standard version at lower cost |\n| No special requirements (default) | `qwen-image-2.0-pro` | Most balanced overall capability |\n\n## Prompt Design Guide\n\n> For detailed guide, see [Prompt Design Guide](references/prompt-guide.md)\n\nPrompt structure formula: `[Subject description] + [Scene/environment] + [Lighting/mood] + [Composition/angle] + [Art style] + [Quality parameters]`\n\n**Complete example**:\n```\nA 30-year-old Asian male detective standing on a rainy night Tokyo street, wearing a dark gray trench coat, holding a black umbrella, neon red and blue lights reflecting on his face, raindrops dripping along the umbrella edge, puddles on the ground reflecting brilliant city lights, Rembrandt lighting, volumetric fog, cinematic composition, 35mm anamorphic lens, film grain, moody atmosphere, high detail, photorealistic, 8K\n```\n\n**Negative prompt template** (Qwen series supports `negative_prompt`, Wanx series does not):\n```\nlow quality, blurry, pixelated, oversaturated, deformed hands, extra fingers, distorted face, uncanny valley, text, watermark, logo, signature, cropped, out of frame, worst quality\n```\n\n---\n\n## Reference Information\n\n### Environment & Dependencies\n\n| Package | Version Constraint | Purpose |\n|---------|-------------------|---------|\n| `dashscope` | `==1.25.16` | Text-to-image, image editing, Wanx generation |\n| `openai` | `==2.23.0` | Image understanding (OpenAI-compatible interface) |\n\n### API Key Configuration\n\nThis skill manages DashScope API Keys through `scripts/api_key.py`, which automatically validates key format (detects invalid keys like `sk-sp-`).\n\n**Retrieval Priority**: `~/.aliyun/config.json` > Environment variable `DASHSCOPE_API_KEY` > Auto-create via Alibaba Cloud CLI\n\n**Method 1: Alibaba Cloud CLI Config File (Recommended)**\n\nAdd a `dashscope` field to the current profile in `~/.aliyun/config.json`:\n```json\n{\n \"current\": \"default\",\n \"profiles\": [\n {\n \"name\": \"default\",\n \"mode\": \"AK\",\n \"access_key_id\": \"...\",\n \"access_key_secret\": \"...\",\n \"dashscope\": {\n \"api_key\": \"sk-xxx\",\n \"api_key_id\": \"4359606\"\n }\n }\n ]\n}\n```\n\n**Method 2: System Environment Variable**\n\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support image generation) |\n| **Get Key** | https://help.aliyun.com/zh/model-studio/get-api-key |\n| **Security** | Never hardcode; use `from api_key import get_api_key`; **never output full API Key values to terminal, logs, or any files** |\n\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\n`scripts/api_key.py` creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n\n# Install ModelStudio plugin (if not already installed)\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n\n# Disable AI-Mode after task completion\naliyun configure ai-mode disable\n```\n\n| Command | Purpose | Called From |\n|---------|---------|------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio list-api-keys` | List existing API Keys (used for limit recycling) | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: delete_api_key()` / `generate_api_key()` |\n\n### Regions & API Endpoints\n\n| Region | DashScope API | OpenAI Compatible Mode |\n|--------|---------------|------------------------|\n| Beijing (China) | `https://dashscope.aliyuncs.com/api/v1` | `https://dashscope.aliyuncs.com/compatible-mode/v1` |\n| Singapore | `https://dashscope-intl.aliyuncs.com/api/v1` | `https://dashscope-intl.aliyuncs.com/compatible-mode/v1` |\n| Virginia (US) | `https://dashscope-us.aliyuncs.com/api/v1` | `https://dashscope-us.aliyuncs.com/compatible-mode/v1` |\n\n**Note**: API Keys are not interchangeable between regions.\n\n### Script Parameter Reference\n\n#### text_to_image.py -- Text-to-Image (Qwen Series)\n\n| Parameter | Description |\n|-----------|-------------|\n| `size` | Only supports `1024*1024`, `720*1280`, `1280*720`, `1440*720`, `720*1440` |\n| `prompt_extend` | True to auto-optimize prompts |\n| `negative_prompt` | Negative prompt to avoid generating undesired content |\n| `watermark` | Whether to add watermark |\n\n#### image_edit.py / image_edit_base64.py -- Image Editing (Qwen Series)\n\n| Parameter | Type | Description |\n|-----------|------|-------------|\n| `n` | int | Number of output images, 1-6 (`qwen-image-edit` fixed at 1) |\n| `size` | str | Output resolution, format `\"width*height\"`, range [512, 2048] |\n| `negative_prompt` | str | Negative prompt |\n| `prompt_extend` | bool | Smart prompt rewriting, default true |\n| `watermark` | bool | Whether to add watermark, default false |\n| `seed` | int | Random seed [0, 2147483647], for result reproduction |\n\n**Editing Model Comparison**:\n\n| Model | Strengths | Use Case |\n|-------|-----------|----------|\n| `qwen-image-edit-max` | Strong geometric reasoning, character consistency | Product design, precise editing |\n| `qwen-image-2.0-pro` | Strong text rendering, realistic quality | General editing, text editing |\n| `qwen-image-edit-plus` | Multi-image output and custom resolution | General editing |\n| `qwen-image-edit` | Basic version, fixed single image output | Simple editing |\n\n**Common Resolution Recommendations**:\n\n| Aspect Ratio | Recommended Resolution |\n|-------------|----------------------|\n| 1:1 | `1024*1024`, `1536*1536` |\n| 2:3 | `768*1152`, `1024*1536` |\n| 3:2 | `1152*768`, `1536*1024` |\n| 3:4 | `960*1280`, `1080*1440` |\n| 4:3 | `1280*960`, `1440*1080` |\n| 9:16 | `720*1280`, `1080*1920` |\n| 16:9 | `1280*720`, `1920*1080` |\n| 21:9 | `1344*576`, `2048*872` |\n\n**Image Input Methods**:\n- **HTTP/HTTPS URL**: `\"https://example.com/image.png\"`\n- **Base64 Encoding**: `\"data:image/png;base64,...\"` -- See `scripts/image_edit_base64.py`\n- Input: 1-3 reference images; Output: 1-6 images (controlled via `n` parameter)\n\n#### wanx_generate.py -- Wanx Image Generation\n\n| Parameter | Description |\n|-----------|-------------|\n| `size` | `1K`, `2K`, `4K` (4K only for `wan2.7-image-pro` text-to-image) |\n| `enable_sequential` | Series mode, generates 1-12 style-consistent images |\n| `thinking_mode` | Default true, enhances reasoning quality (increases latency) |\n| `n` | Normal mode 1-4, series mode 1-12 |\n\n| Model | Strengths |\n|-------|-----------|\n| `wan2.7-image-pro` | Pro version, text-to-image supports 4K HD |\n| `wan2.7-image` | Standard version, faster generation |\n\n#### image_understanding.py -- Image Understanding\n\nUses OpenAI-compatible mode to call DashScope. Default model `qwen3.5-plus` (general understanding), optional `qwen-vl-max` (complex reasoning).\n\n### Pricing\n\n| Model | Unit Price | Notes |\n|-------|-----------|-------|\n| qwen-image-2.0-pro | CN price | Pro version, strongest text rendering and realistic quality |\n| qwen-image-edit-max | CN price | Pro version, image generation and editing fusion |\n| qwen-image-2.0 | CN price | Standard version |\n| qwen-image-edit-plus | CN price | Standard editing |\n| wan2.7-image-pro | CN price | Wanx pro version |\n| wan2.7-image | CN price | Wanx standard version |\n\nNew users get 100 free image credits (valid for 90 days) after activating Bailian. View bills: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Best Practices\n\n1. Use `prompt_extend=True` to let the model auto-optimize prompts\n2. Qwen series supports `negative_prompt`, Wanx series does not support this parameter\n3. Choose appropriate sizes based on requirements to avoid unnecessary large image generation\n4. Use the `n` parameter to generate multiple images at once for selection\n5. Use URLs for online images, Base64 for small local files, `file://` protocol for large files\n\n### Error Handling\n\nScripts have built-in error handling logic. For errors, refer to: https://help.aliyun.com/zh/model-studio/developer-reference/error-code\n\n### Related Resources\n\n- [Prompt Design Guide](references/prompt-guide.md) - Professional prompt construction methods\n- [API Documentation](references/api-docs.md) - Detailed API parameter reference\n- [Model List](references/models.md) - Supported models and features\n- [Error Code Reference](references/error-codes.md) - Common error codes and solutions\n- [Example Scripts](scripts/) - Ready-to-use scripts\n\n| Script | Function | Description |\n|--------|----------|-------------|\n| `text_to_image.py` | Text-to-image | Generate images from text descriptions, supports auto-download |\n| `image_edit.py` | Image editing | Supports URL, local file, and Base64 input methods |\n| `image_edit_base64.py` | Base64 image editing | Demonstrates Base64 encoding method |\n| `base64_tool.py` | Base64 tool | CLI tool for image-Base64 conversion |\n| `wanx_generate.py` | Wanx generation | Uses wan2.7-image model for generation |\n| `image_understanding.py` | Image understanding | Analyze image content, answer questions |\n"}}, "skills/aiml/sfm/alibabacloud-bailian-rag-knowledgebase/SKILL.md": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674227.6561131, "n": 10056, "h": "1b2afc992de32ff9", "c": "---\r\nname: alibabacloud-bailian-rag-knowledgebase\r\ndescription: \"Alibaba Cloud Bailian Knowledge Base Retrieval Tool. Use HTTPS API to query and retrieve knowledge base content. Use when: User needs to query knowledge base, retrieve document content, or answer questions based on knowledge base. Prerequisites: (1) Configure DashScope API Key (2) Activate Bailian Knowledge Base service.\"\r\n---\r\n\r\n# Bailian Knowledge Base Retrieval\r\n\r\nThis Skill provides query and retrieval capabilities for Alibaba Cloud Bailian Knowledge Base via HTTPS API.\r\n\r\n## API Key Security Management\r\n\r\nScripts automatically handle key retrieval via `api_key.py`. The Agent does not need to and should not manually extract, set, or pass API Key values.\r\n\r\n- **Key retrieval is automated**: Scripts internally call `api_key.py` to automatically obtain keys from config files/environment variables. The Agent only needs to run the script command.\r\n- **Never hardcode any form of key**: Including `api_key = \"sk-...\"`, `export DASHSCOPE_API_KEY=\"sk-...\"`, and assigning keys in shell scripts.\r\n- **Never extract keys from CLI output**: The Agent must not write key values into any script, variable, or file.\r\n- **Never expose keys in any output**: Including generated scripts, shell commands, log files, and terminal output containing strings starting with `sk-`.\r\n- **Never read or print keys from config files**: Do not use `cat`, `jq`, `python -c`, or other commands to read and output API Key values.\r\n- **Mandatory self-check before task completion**: Run `grep -rn \"sk-\" /` to check all output files; if any strings starting with `sk-` are found (excluding `sk-xxx` placeholders), delete the affected files and regenerate.\r\n\r\n## ๐Ÿš€ Initial Setup (Required for First-time Use)\r\n\r\n### 1. Configure API Key\r\n\r\nAPI Keys are managed by the unified `scripts/api_key.py` module, with the following retrieval priority:\r\n1. Alibaba Cloud CLI config `~/.aliyun/config.json` current profile's `dashscope.api_key`\r\n2. Environment variable `DASHSCOPE_API_KEY`\r\n3. Auto-create and save when Alibaba Cloud CLI is available (`generate_api_key()`)\r\n\r\n```python\r\n# All scripts use this unified approach\r\nfrom api_key import get_api_key\r\napi_key = get_api_key() # Returns str, raises ValueError if not found\r\n```\r\n\r\nManual environment variable configuration:\r\n```bash\r\nexport DASHSCOPE_API_KEY=sk-xxx\r\n```\r\n\r\n| Item | Description |\r\n|------|-------------|\r\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\r\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support knowledge base services) |\r\n| **Get Key** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\r\n\r\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\r\n\r\nThe `scripts/api_key.py` module creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\r\n\r\n**1. Update Plugins**\r\n\r\n```bash\r\n# Update plugins to latest version\r\naliyun plugin update\r\n```\r\n\r\n**2. Install ModelStudio Plugin** (if not already installed)\r\n\r\n```bash\r\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\r\n```\r\n\r\n**CLI Commands Used**:\r\n\r\n| Command | Purpose | Called From |\r\n|---------|---------|-------------|\r\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\r\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\r\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: _delete_cloud_api_key()` |\r\n\r\n### 2. Important Prerequisite: Activate Bailian Knowledge Base Service\r\n\r\n**Before using this skill, you must first activate the knowledge base service in the Alibaba Cloud Bailian console!**\r\n\r\n1. Visit [Bailian Knowledge Base page](https://bailian.console.aliyun.com/cn-beijing/?tab=app#/knowledge-base)\r\n2. Click the **\"Activate Now\"** button\r\n3. Confirm activation and wait for the service to take effect (usually 1-2 minutes)\r\n\r\n## Observability (MUST follow for every aliyun command)\r\n\r\nUpon loading this skill, generate a random session ID (32-char lowercase hex string) and export it as `SKILL_SESSION_ID`. This value must remain constant for the entire skill session.\r\n\r\nEvery `aliyun` CLI command and HTTPS API call must include a `User-Agent` identifier for traceability.\r\n\r\n### User-Agent Format\r\n\r\n```\r\nAlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session-id}\r\n```\r\n\r\n- `{session-id}` = value of `SKILL_SESSION_ID` environment variable (32-char lowercase hex string)\r\n- Scripts read it via `os.environ.get(\"SKILL_SESSION_ID\", \"\")`; if empty, generate one with `secrets.token_hex(16)`\r\n\r\n### Per-Command --user-agent (CLI)\r\n\r\nEvery `aliyun modelstudio` business command MUST carry `--user-agent` on the command line:\r\n\r\n```bash\r\naliyun modelstudio list-workspaces --region cn-beijing \\\r\n --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session-id}\"\r\n```\r\n\r\n> **Prohibited**: Do NOT set User-Agent via global configuration commands. Only per-command `--user-agent` is allowed.\r\n\r\n### HTTPS User-Agent Header\r\n\r\nAll HTTPS API requests set the `User-Agent` HTTP header:\r\n\r\n```python\r\n\"User-Agent\": f\"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session_id}\"\r\n```\r\n\r\n### Applied Locations\r\n\r\n| Location | Mechanism |\r\n|----------|----------|\r\n| `scripts/list_indices.py` | HTTPS `User-Agent` header via `_get_user_agent()` |\r\n| `scripts/retrieve.py` | HTTPS `User-Agent` header via `_get_user_agent()` |\r\n| `scripts/api_key.py` โ†’ `_get_workspace_id()` | `aliyun modelstudio list-workspaces --user-agent ...` |\r\n| `scripts/api_key.py` โ†’ `generate_api_key()` | `aliyun modelstudio create-api-key --user-agent ...` |\r\n| `scripts/api_key.py` โ†’ `_delete_cloud_api_key()` | `aliyun modelstudio delete-api-key --user-agent ...` |\r\n\r\n## Available Scripts\r\n\r\nAll scripts are located in the `scripts/` directory:\r\n\r\n| Script | Purpose | Parameters |\r\n|--------|---------|------------|\r\n| `api_key.py` | API Key management (get, create, delete) | - |\r\n| `list_indices.py` | Query knowledge base list | `[page_number] [page_size]` |\r\n| `retrieve.py` | Retrieve from specified knowledge base | `index_id query [top_n]` |\r\n\r\n## Workflow\r\n\r\n### Step 1: Query Knowledge Base List\r\n\r\nRun `scripts/list_indices.py` to get all available knowledge bases:\r\n\r\n```bash\r\npython3 scripts/list_indices.py\r\n```\r\n\r\nReturn format:\r\n```json\r\n[\r\n {\r\n \"id\": \"qf91w6402d\",\r\n \"name\": \"Product Documentation\",\r\n \"description\": \"Contains product user manuals, API documentation, etc.\"\r\n },\r\n {\r\n \"id\": \"ip93d2pyvz\",\r\n \"name\": \"Customer Service Q&A\",\r\n \"description\": \"FAQ, customer service scripts\"\r\n }\r\n]\r\n```\r\n\r\n**Pagination:** page_number starts from 1 (default), page_size defaults to 10. If current page is not fully retrieved, continue to retrieve next page:\r\n```bash\r\npython3 scripts/list_indices.py 2 10\r\n```\r\n\r\n### Step 2: Intelligent Knowledge Base Selection\r\n\r\nBased on the user's question and knowledge base descriptions, select **1-3 most relevant knowledge bases** for retrieval.\r\n\r\nSelection Strategy:\r\n- Match keywords (keywords in question vs knowledge base name/description)\r\n- Prioritize knowledge bases that explicitly contain relevant fields in their descriptions\r\n- If uncertain, select all or let user manually select\r\n\r\n### Step 3: Execute Retrieval\r\n\r\nFor each selected knowledge base, run `scripts/retrieve.py index_id query [top_n]`:\r\n\r\n```bash\r\npython3 scripts/retrieve.py lj3hgbq60t \"java\" 5\r\n```\r\n\r\nParameters:\r\n- `index_id` (required): Knowledge base ID\r\n- `query` (required): Search query text\r\n- `top_n` (optional): Number of top results to return, default 5, max 20\r\n\r\nThe retrieve API uses the following configuration:\r\n- `dense_similarity_top_k`: 100\r\n- `sparse_similarity_top_k`: 100\r\n- `enable_reranking`: true\r\n- `rerank`: qwen3-rerank-hybrid with similar mode\r\n\r\nReturn format, content inside each chunk represents chunk content, doc_name represents source document, score represents match score, title represents chunk section title:\r\n```json\r\n{\r\n \"indexId\": \"lj3hgbq60t\",\r\n \"chunks\": [\r\n {\r\n \"content\": \"Document chunk content...\",\r\n \"score\": 0.6040189862251282,\r\n \"doc_name\": \"example-doc.pdf\",\r\n \"title\": \"Section Title\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\n### Step 4: Integrate Answer\r\n\r\nBased on retrieval results:\r\n1. Sort by relevance (score descending)\r\n2. Extract key information\r\n3. Organize answer in natural language\r\n4. Please annotate the information source at the end of the generated answer (knowledge base name; document name; section name), can reference multiple documents and sections.\r\n\r\n## Common Errors\r\n\r\n### 401 Unauthorized\r\n```json\r\n{\"code\": \"InvalidApiKey\", \"message\": \"Invalid API-KEY\"}\r\n```\r\nAPI Key is incorrect or not configured. Guide user to check their API Key configuration.\r\n\r\n### 403 Forbidden\r\n```json\r\n{\"code\": \"Forbidden\", \"message\": \"Service not activated\"}\r\n```\r\nUser has not activated the Bailian Knowledge Base service. Guide user to activate it.\r\n\r\n## Usage Example\r\n\r\n**User:** \"What authentication methods does our product support?\"\r\n\r\n**Flow:**\r\n1. Query knowledge base โ†’ Returns 3 knowledge bases\r\n2. Select knowledge base โ†’ \"Product Documentation\" (most relevant)\r\n3. Retrieve โ†’ Get authentication-related document chunks\r\n4. Answer โ†’ \"According to product documentation, OAuth2.0, SAML, and API Key authentication methods are supported...\"\r\n\r\n## Notes\r\n\r\n- API Key is automatically retrieved by scripts, Agent should never handle key values directly\r\n- When retrieving from multiple knowledge bases, merge results and deduplicate\r\n- Sort retrieval results by score, prioritize high-relevance content\r\n\r\n**API Key Auto-Retrieval Flow**:\r\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` โ†’ Return if found\r\n2. Read environment variable `DASHSCOPE_API_KEY` โ†’ Return if found\r\n3. Alibaba Cloud CLI available โ†’ Auto-create via `generate_api_key()` and save to config\r\n4. All above fail โ†’ Error with setup instructions\r\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846854.0691907, "n": 10056, "h": "1b2afc992de32ff9", "c": "---\r\nname: alibabacloud-bailian-rag-knowledgebase\r\ndescription: \"Alibaba Cloud Bailian Knowledge Base Retrieval Tool. Use HTTPS API to query and retrieve knowledge base content. Use when: User needs to query knowledge base, retrieve document content, or answer questions based on knowledge base. Prerequisites: (1) Configure DashScope API Key (2) Activate Bailian Knowledge Base service.\"\r\n---\r\n\r\n# Bailian Knowledge Base Retrieval\r\n\r\nThis Skill provides query and retrieval capabilities for Alibaba Cloud Bailian Knowledge Base via HTTPS API.\r\n\r\n## API Key Security Management\r\n\r\nScripts automatically handle key retrieval via `api_key.py`. The Agent does not need to and should not manually extract, set, or pass API Key values.\r\n\r\n- **Key retrieval is automated**: Scripts internally call `api_key.py` to automatically obtain keys from config files/environment variables. The Agent only needs to run the script command.\r\n- **Never hardcode any form of key**: Including `api_key = \"sk-...\"`, `export DASHSCOPE_API_KEY=\"sk-...\"`, and assigning keys in shell scripts.\r\n- **Never extract keys from CLI output**: The Agent must not write key values into any script, variable, or file.\r\n- **Never expose keys in any output**: Including generated scripts, shell commands, log files, and terminal output containing strings starting with `sk-`.\r\n- **Never read or print keys from config files**: Do not use `cat`, `jq`, `python -c`, or other commands to read and output API Key values.\r\n- **Mandatory self-check before task completion**: Run `grep -rn \"sk-\" /` to check all output files; if any strings starting with `sk-` are found (excluding `sk-xxx` placeholders), delete the affected files and regenerate.\r\n\r\n## ๐Ÿš€ Initial Setup (Required for First-time Use)\r\n\r\n### 1. Configure API Key\r\n\r\nAPI Keys are managed by the unified `scripts/api_key.py` module, with the following retrieval priority:\r\n1. Alibaba Cloud CLI config `~/.aliyun/config.json` current profile's `dashscope.api_key`\r\n2. Environment variable `DASHSCOPE_API_KEY`\r\n3. Auto-create and save when Alibaba Cloud CLI is available (`generate_api_key()`)\r\n\r\n```python\r\n# All scripts use this unified approach\r\nfrom api_key import get_api_key\r\napi_key = get_api_key() # Returns str, raises ValueError if not found\r\n```\r\n\r\nManual environment variable configuration:\r\n```bash\r\nexport DASHSCOPE_API_KEY=sk-xxx\r\n```\r\n\r\n| Item | Description |\r\n|------|-------------|\r\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\r\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support knowledge base services) |\r\n| **Get Key** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\r\n\r\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\r\n\r\nThe `scripts/api_key.py` module creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\r\n\r\n**1. Update Plugins**\r\n\r\n```bash\r\n# Update plugins to latest version\r\naliyun plugin update\r\n```\r\n\r\n**2. Install ModelStudio Plugin** (if not already installed)\r\n\r\n```bash\r\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\r\n```\r\n\r\n**CLI Commands Used**:\r\n\r\n| Command | Purpose | Called From |\r\n|---------|---------|-------------|\r\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\r\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\r\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: _delete_cloud_api_key()` |\r\n\r\n### 2. Important Prerequisite: Activate Bailian Knowledge Base Service\r\n\r\n**Before using this skill, you must first activate the knowledge base service in the Alibaba Cloud Bailian console!**\r\n\r\n1. Visit [Bailian Knowledge Base page](https://bailian.console.aliyun.com/cn-beijing/?tab=app#/knowledge-base)\r\n2. Click the **\"Activate Now\"** button\r\n3. Confirm activation and wait for the service to take effect (usually 1-2 minutes)\r\n\r\n## Observability (MUST follow for every aliyun command)\r\n\r\nUpon loading this skill, generate a random session ID (32-char lowercase hex string) and export it as `SKILL_SESSION_ID`. This value must remain constant for the entire skill session.\r\n\r\nEvery `aliyun` CLI command and HTTPS API call must include a `User-Agent` identifier for traceability.\r\n\r\n### User-Agent Format\r\n\r\n```\r\nAlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session-id}\r\n```\r\n\r\n- `{session-id}` = value of `SKILL_SESSION_ID` environment variable (32-char lowercase hex string)\r\n- Scripts read it via `os.environ.get(\"SKILL_SESSION_ID\", \"\")`; if empty, generate one with `secrets.token_hex(16)`\r\n\r\n### Per-Command --user-agent (CLI)\r\n\r\nEvery `aliyun modelstudio` business command MUST carry `--user-agent` on the command line:\r\n\r\n```bash\r\naliyun modelstudio list-workspaces --region cn-beijing \\\r\n --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session-id}\"\r\n```\r\n\r\n> **Prohibited**: Do NOT set User-Agent via global configuration commands. Only per-command `--user-agent` is allowed.\r\n\r\n### HTTPS User-Agent Header\r\n\r\nAll HTTPS API requests set the `User-Agent` HTTP header:\r\n\r\n```python\r\n\"User-Agent\": f\"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session_id}\"\r\n```\r\n\r\n### Applied Locations\r\n\r\n| Location | Mechanism |\r\n|----------|----------|\r\n| `scripts/list_indices.py` | HTTPS `User-Agent` header via `_get_user_agent()` |\r\n| `scripts/retrieve.py` | HTTPS `User-Agent` header via `_get_user_agent()` |\r\n| `scripts/api_key.py` โ†’ `_get_workspace_id()` | `aliyun modelstudio list-workspaces --user-agent ...` |\r\n| `scripts/api_key.py` โ†’ `generate_api_key()` | `aliyun modelstudio create-api-key --user-agent ...` |\r\n| `scripts/api_key.py` โ†’ `_delete_cloud_api_key()` | `aliyun modelstudio delete-api-key --user-agent ...` |\r\n\r\n## Available Scripts\r\n\r\nAll scripts are located in the `scripts/` directory:\r\n\r\n| Script | Purpose | Parameters |\r\n|--------|---------|------------|\r\n| `api_key.py` | API Key management (get, create, delete) | - |\r\n| `list_indices.py` | Query knowledge base list | `[page_number] [page_size]` |\r\n| `retrieve.py` | Retrieve from specified knowledge base | `index_id query [top_n]` |\r\n\r\n## Workflow\r\n\r\n### Step 1: Query Knowledge Base List\r\n\r\nRun `scripts/list_indices.py` to get all available knowledge bases:\r\n\r\n```bash\r\npython3 scripts/list_indices.py\r\n```\r\n\r\nReturn format:\r\n```json\r\n[\r\n {\r\n \"id\": \"qf91w6402d\",\r\n \"name\": \"Product Documentation\",\r\n \"description\": \"Contains product user manuals, API documentation, etc.\"\r\n },\r\n {\r\n \"id\": \"ip93d2pyvz\",\r\n \"name\": \"Customer Service Q&A\",\r\n \"description\": \"FAQ, customer service scripts\"\r\n }\r\n]\r\n```\r\n\r\n**Pagination:** page_number starts from 1 (default), page_size defaults to 10. If current page is not fully retrieved, continue to retrieve next page:\r\n```bash\r\npython3 scripts/list_indices.py 2 10\r\n```\r\n\r\n### Step 2: Intelligent Knowledge Base Selection\r\n\r\nBased on the user's question and knowledge base descriptions, select **1-3 most relevant knowledge bases** for retrieval.\r\n\r\nSelection Strategy:\r\n- Match keywords (keywords in question vs knowledge base name/description)\r\n- Prioritize knowledge bases that explicitly contain relevant fields in their descriptions\r\n- If uncertain, select all or let user manually select\r\n\r\n### Step 3: Execute Retrieval\r\n\r\nFor each selected knowledge base, run `scripts/retrieve.py index_id query [top_n]`:\r\n\r\n```bash\r\npython3 scripts/retrieve.py lj3hgbq60t \"java\" 5\r\n```\r\n\r\nParameters:\r\n- `index_id` (required): Knowledge base ID\r\n- `query` (required): Search query text\r\n- `top_n` (optional): Number of top results to return, default 5, max 20\r\n\r\nThe retrieve API uses the following configuration:\r\n- `dense_similarity_top_k`: 100\r\n- `sparse_similarity_top_k`: 100\r\n- `enable_reranking`: true\r\n- `rerank`: qwen3-rerank-hybrid with similar mode\r\n\r\nReturn format, content inside each chunk represents chunk content, doc_name represents source document, score represents match score, title represents chunk section title:\r\n```json\r\n{\r\n \"indexId\": \"lj3hgbq60t\",\r\n \"chunks\": [\r\n {\r\n \"content\": \"Document chunk content...\",\r\n \"score\": 0.6040189862251282,\r\n \"doc_name\": \"example-doc.pdf\",\r\n \"title\": \"Section Title\"\r\n }\r\n ]\r\n}\r\n```\r\n\r\n### Step 4: Integrate Answer\r\n\r\nBased on retrieval results:\r\n1. Sort by relevance (score descending)\r\n2. Extract key information\r\n3. Organize answer in natural language\r\n4. Please annotate the information source at the end of the generated answer (knowledge base name; document name; section name), can reference multiple documents and sections.\r\n\r\n## Common Errors\r\n\r\n### 401 Unauthorized\r\n```json\r\n{\"code\": \"InvalidApiKey\", \"message\": \"Invalid API-KEY\"}\r\n```\r\nAPI Key is incorrect or not configured. Guide user to check their API Key configuration.\r\n\r\n### 403 Forbidden\r\n```json\r\n{\"code\": \"Forbidden\", \"message\": \"Service not activated\"}\r\n```\r\nUser has not activated the Bailian Knowledge Base service. Guide user to activate it.\r\n\r\n## Usage Example\r\n\r\n**User:** \"What authentication methods does our product support?\"\r\n\r\n**Flow:**\r\n1. Query knowledge base โ†’ Returns 3 knowledge bases\r\n2. Select knowledge base โ†’ \"Product Documentation\" (most relevant)\r\n3. Retrieve โ†’ Get authentication-related document chunks\r\n4. Answer โ†’ \"According to product documentation, OAuth2.0, SAML, and API Key authentication methods are supported...\"\r\n\r\n## Notes\r\n\r\n- API Key is automatically retrieved by scripts, Agent should never handle key values directly\r\n- When retrieving from multiple knowledge bases, merge results and deduplicate\r\n- Sort retrieval results by score, prioritize high-relevance content\r\n\r\n**API Key Auto-Retrieval Flow**:\r\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` โ†’ Return if found\r\n2. Read environment variable `DASHSCOPE_API_KEY` โ†’ Return if found\r\n3. Alibaba Cloud CLI available โ†’ Auto-create via `generate_api_key()` and save to config\r\n4. All above fail โ†’ Error with setup instructions\r\n"}}, "skills/aiml/sfm/alibabacloud-bailian-rag-knowledgebase/scripts/api_key.py": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674227.7521877, "n": 16740, "h": "4a0a0d6e5bdc1264", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport secrets\nimport subprocess\nfrom pathlib import Path\nfrom typing import Optional, Tuple\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _get_user_agent() -> str:\n \"\"\"Build User-Agent string with session-id from SKILL_SESSION_ID env var.\"\"\"\n session_id = os.environ.get(\"SKILL_SESSION_ID\", \"\")\n if not session_id:\n session_id = secrets.token_hex(16)\n os.environ[\"SKILL_SESSION_ID\"] = session_id\n return f\"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session_id}\"\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> Optional[dict]:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"Installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n if not _install_modelstudio_plugin():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-rag-knowledgebase\") -> Tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n messages.append(\"Cloud API Key ID not found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support knowledge base services\\n\"\n \" - Knowledge base services require a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846855.5026016, "n": 16740, "h": "4a0a0d6e5bdc1264", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport secrets\nimport subprocess\nfrom pathlib import Path\nfrom typing import Optional, Tuple\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _get_user_agent() -> str:\n \"\"\"Build User-Agent string with session-id from SKILL_SESSION_ID env var.\"\"\"\n session_id = os.environ.get(\"SKILL_SESSION_ID\", \"\")\n if not session_id:\n session_id = secrets.token_hex(16)\n os.environ[\"SKILL_SESSION_ID\"] = session_id\n return f\"AlibabaCloud-Agent-Skills/alibabacloud-bailian-rag-knowledgebase/{session_id}\"\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> Optional[dict]:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"Installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n if not _install_modelstudio_plugin():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-rag-knowledgebase\") -> Tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", _get_user_agent()],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n messages.append(\"Cloud API Key ID not found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support knowledge base services\\n\"\n \" - Knowledge base services require a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key"}}, "skills/aiml/sfm/alibabacloud-bailian-voice-creator/SKILL.md": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674227.7580545, "n": 18468, "h": "982f03a19ebd2c18", "c": "---\nname: alibabacloud-bailian-voice-creator\ndescription: AI voice creation skill supporting speech recognition (ASR) and text-to-speech (TTS). Uses qwen3-asr-flash-filetrans, qwen-tts and other models. Use this skill when users need speech-to-text, text-to-speech, or audio processing. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Voice Creator\n\nProfessional-grade AI voice creation skill supporting speech recognition (ASR) and text-to-speech (TTS). Built on Alibaba Cloud DashScope API.\n\n## Security Rules (Mandatory)\n\n1. **Never hardcode or log API Keys in plain text** in code, logs, or output files. Patterns like `api_key = \"sk-...\"` are strictly forbidden.\n2. API Keys must only be obtained via `scripts/api_key.py`'s `get_api_key()` function, or via `os.environ.get('DASHSCOPE_API_KEY')`.\n3. Generated scripts, logs, and output files must not contain any content starting with `sk-`.\n4. Logs should only record operation status (success/failure), never credential information.\n\n## Execution Requirements (Mandatory)\n\n1. **TTS must and can only use `dashscope.MultiModalConversation.call` with the `qwen-tts` model.** Using edge-tts, gTTS, ElevenLabs, Azure TTS, sambert, NLS, or any other third-party TTS service is strictly forbidden.\n2. **Never generate mock/demo code or placeholder files.** Real API calls must be made; do not generate simulated scripts or blank audio files for any reason.\n3. **Never auto-fallback when API calls fail.** Do not switch to other TTS services after a qwen-tts failure. Catch the exception, output a clear error message, and exit.\n4. If the `dashscope` library is missing, install it first with `pip install dashscope`.\n\n## Required API Call Templates (Do Not Replace)\n\n### Standard Speech Synthesis\n\n```python\nimport dashscope\nfrom api_key import get_api_key\n\napi_key = get_api_key()\nif api_key:\n dashscope.api_key = api_key\n# If get_api_key() returns None, SDK resolves auth via environment (AK/SK, etc.)\n\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\"\n)\naudio_url = response.output.get('audio', {}).get('url', '')\n```\n\n### Instruct-Controlled Speech Synthesis (Required when user requests a specific voice style)\n\n```python\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\",\n # NOTE: instructions value must be in Chinese - the qwen-tts model processes Chinese instructions\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ\"\n)\n```\n\n**Note: The `instructions` parameter controls voice style via natural language. Do NOT substitute it with `speech_rate`, `pitch_rate`, or `volume_rate` numeric parameters.**\n\n### Error Handling Template\n\n```python\nimport sys\n\ntry:\n response = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\", text=text, voice=voice\n )\n if response.status_code != 200:\n print(f\"qwen-tts call failed: {response.code} - {response.message}\")\n sys.exit(1)\nexcept Exception as e:\n print(f\"qwen-tts call failed: {e}\")\n print(\"Please check: 1) Is DASHSCOPE_API_KEY set? 2) Is the network available?\")\n sys.exit(1)\n# Do NOT fallback to edge-tts, gTTS or other services here\n```\n\n## Feature Overview\n\n| Feature | Model | Highlights |\n|---------|-------|------------|\n| Long Audio Recognition | `qwen3-asr-flash-filetrans` | Up to 12 hours, supports emotion detection & timestamps |\n| Short Audio Recognition | `qwen3-asr-flash` | Up to 5 minutes, low latency |\n| Speech Synthesis | `qwen-tts` | Multiple voices, multilingual, instruction control |\n| Instruct-Controlled Synthesis | `qwen-tts` + instructions | Control voice expressiveness via natural language |\n\n## Orchestration Logic\n\n### Products and APIs\n\n| Product | API / SDK Call | Purpose |\n|---------|---------------|---------|\n| DashScope ASR | `Transcription.async_call` + `Transcription.wait` | Long audio recognition (async) |\n| DashScope ASR | `POST /services/audio/asr/transcription` | Short audio recognition (sync) |\n| DashScope TTS | `MultiModalConversation.call` | Speech synthesis (standard / instruct-controlled) |\n| Alibaba Cloud CLI ModelStudio | `create-api-key` / `list-workspaces` / `delete-api-key` | API Key lifecycle management |\n\n### Decision Flow\n\n```\nUser Request\n |\n +-- Intent: Audio -> Text (ASR)\n | |\n | +-- Audio duration <= 5 min AND file <= 10MB AND no emotion/timestamps needed?\n | | -> Short audio recognition: qwen3-asr-flash (sync, low latency)\n | |\n | +-- Other cases (long audio / emotion detection / timestamps needed)\n | -> Long audio recognition: qwen3-asr-flash-filetrans (async, submit + poll)\n |\n +-- Intent: Text -> Speech (TTS)\n | |\n | +-- User specified voice style/emotion/speed requirements?\n | | -> Instruct-controlled synthesis: qwen-tts + instructions parameter\n | |\n | +-- Standard reading only\n | -> Standard synthesis: qwen-tts\n |\n +-- Prerequisite: No available API Key\n -> Call api_key.py: get_api_key() auto-reads\n -> If none exists: generate_api_key() creates via Alibaba Cloud CLI and saves\n```\n\n### Call Sequence\n\n**Speech Recognition (Long Audio)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `Transcription.async_call(model, file_urls, language_hints)` -> Submit async task, get task_id\n3. `Transcription.wait(task=task_id)` -> Poll until task completes\n4. Get recognition result JSON from `output.results[].transcription_url`\n5. Parse `transcripts[].text` / `sentences[]` / `emotion` from JSON\n\n**Speech Recognition (Short Audio)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `POST /services/audio/asr/transcription` -> Sync call, returns recognized text directly\n\n**Speech Synthesis (Standard / Instruct-Controlled)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `MultiModalConversation.call(model, text, voice, [instructions])` -> Returns audio URL\n3. `download_audio(url, output_path)` -> Download audio and auto-detect format (WAV/MP3)\n\n**API Key Auto-Retrieval**:\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` -> Return if found\n2. Read environment variable `DASHSCOPE_API_KEY` -> Return if found\n3. Alibaba Cloud CLI available -> Auto-create via `generate_api_key()` and save to config\n4. All above fail -> Error with setup instructions\n\n### Quick Reference\n\n| Condition | Choice |\n|-----------|--------|\n| Audio <= 5 min and <= 10MB | `qwen3-asr-flash` |\n| Audio > 5 min or > 10MB | `qwen3-asr-flash-filetrans` |\n| Need emotion detection / timestamps / punctuation | `qwen3-asr-flash-filetrans` |\n| TTS with no style requirements | `qwen-tts` standard call |\n| TTS with style/emotion/speed requirements | `qwen-tts` + `instructions` |\n| Need dialect voices | Not supported by current `qwen-tts`; pending model update or other TTS models |\n\n## Speech Recognition (ASR) Guide\n\n### Model Selection\n\n| Scenario | Recommended Model | Notes |\n|----------|------------------|-------|\n| Meeting transcription, interview records | `qwen3-asr-flash-filetrans` | Long audio, supports emotion detection & timestamps |\n| Voice messages, real-time subtitles | `qwen3-asr-flash` | Short audio, low latency |\n| Customer service QA | `qwen3-asr-flash-filetrans` | Can analyze customer emotions |\n| Singing audio analysis | `qwen3-asr-flash-filetrans` | Supports lyrics recognition & emotion analysis |\n\n### Supported Languages\n\nChinese (Mandarin, Sichuan dialect, Minnan, Wu, Cantonese), English, Japanese, German, Korean, Russian, French, Portuguese, Arabic, Italian, Spanish, Hindi, Indonesian, Thai, Turkish, Ukrainian, Vietnamese, and 30+ other languages.\n\n### Supported Audio Formats\n\n`aac`, `amr`, `avi`, `flac`, `flv`, `m4a`, `mkv`, `mov`, `mp3`, `mp4`, `mpeg`, `ogg`, `opus`, `wav`, `webm`, `wma`, `wmv`\n\n### Feature Comparison\n\n| Feature | qwen3-asr-flash-filetrans | qwen3-asr-flash |\n|---------|---------------------------|-----------------|\n| Audio Duration | Up to 12 hours (<=2GB) | Up to 5 minutes (<=10MB) |\n| Emotion Detection | Supported (Surprise/Calm/Happy/Sad/Disgust/Angry/Fear) | Not supported |\n| Timestamps | Supported (sentence/word level) | Not supported |\n| Punctuation Prediction | Supported | Not supported |\n| Singing Recognition | Supported | Not supported |\n| Noise Rejection | Supported | Not supported |\n\n## Text-to-Speech (TTS) Guide\n\n### Model Selection\n\n| Scenario | Recommended Model | Notes |\n|----------|------------------|-------|\n| Audiobooks, radio drama dubbing | `qwen-tts` + instructions | Supports instruction control, rich expressiveness |\n| Navigation, notification announcements | `qwen-tts` | Short text, high frequency calls |\n| Online education courseware | `qwen-tts` | Multilingual support |\n\n**Important Notes**:\n- Speech synthesis uses the `MultiModalConversation.call` API\n- Audio output is in WAV format (URL valid for 24 hours)\n- The script auto-detects format and saves with the correct extension\n\n### Instruct Control (Instruct)\n\nWhen users request a specific voice style (e.g., livestream sales style, gentle style, news broadcast, etc.), **the `instructions` parameter must be used** to control voice expressiveness via natural language.\n\n**Difference between `instructions` and traditional numeric parameters**:\n- `instructions`: Natural language description, e.g., `\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…\"` -> **Must use this approach**\n- `speech_rate` / `pitch_rate` / `volume_rate`: Numeric parameters -> **Forbidden, qwen-tts does not support these parameters**\n\n**Call method** (follow strictly):\n```python\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\",\n # NOTE: instructions value must be in Chinese - the qwen-tts model processes Chinese instructions\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ๏ผŒ้Ÿณ่ฐƒๅ้ซ˜\"\n)\n```\n\n**Description dimensions reference**:\n\n| Dimension | Examples |\n|-----------|---------|\n| Pitch | High, medium, low, slightly high, slightly low |\n| Speed | Fast, medium, slow, slightly fast, slightly slow |\n| Emotion | Cheerful, calm, gentle, serious, lively, cool, healing |\n| Characteristics | Magnetic, crisp, husky, mellow, sweet, deep, powerful |\n| Use Case | News broadcast, ad voiceover, audiobook, animation character, voice assistant |\n\n**Instruction examples** (in Chinese, as required by the model):\n```\n่ฏญ้€Ÿ่พƒๅฟซ๏ผŒๅธฆๆœ‰ๆ˜Žๆ˜พ็š„ไธŠๆ‰ฌ่ฏญ่ฐƒ๏ผŒ้€‚ๅˆไป‹็ปๆ—ถๅฐšไบงๅ“\n้Ÿณ้‡็”ฑๆญฃๅธธๅฏน่ฏ่ฟ…้€Ÿๅขžๅผบ่‡ณ้ซ˜ๅ–Š๏ผŒๆ€งๆ ผ็›ด็އ๏ผŒๆƒ…็ปชๆ˜“ๆฟ€ๅŠจ\nๅ“ญ่…”ๅฏผ่‡ดๅ‘้Ÿณ็•ฅๅพฎๅซ็ณŠ๏ผŒ็•ฅๆ˜พๆฒ™ๅ“‘๏ผŒๅธฆๆœ‰ๆ˜Žๆ˜พๅ“ญ่…”็š„็ดงๅผ ๆ„Ÿ\n้Ÿณ่ฐƒๅ้ซ˜๏ผŒ่ฏญ้€Ÿไธญ็ญ‰๏ผŒๅ……ๆปกๆดปๅŠ›ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ้€‚ๅˆๅนฟๅ‘Š้…้Ÿณ\n```\n\n### Available Voices (qwen-tts)\n\nWhen calling `qwen-tts` via `MultiModalConversation.call`, the following 4 voices are supported:\n\n| voice Parameter | Voice Name | Description |\n|----------------|------------|-------------|\n| `Cherry` | Qianyue | Sunny, positive, naturally approachable young woman (Female) |\n| `Serena` | Suyao | Gentle young woman (Female) |\n| `Ethan` | Chenxu | Sunny, warm, energetic (Male) |\n| `Chelsie` | Qianxue | Anime-style virtual companion (Female) |\n\n> **Note**: Other voices (Jennifer, Ryan, Neil, Elias, and dialect voices) require the `qwen3-tts-flash` model's `SpeechSynthesizer` WebSocket API, which is not currently supported by these scripts.\n\n## Environment Setup\n\n### 1. Install FFmpeg (Audio Processing Tool)\n\nFFmpeg is used for audio format conversion, sample rate adjustment, and other preprocessing tasks.\n\n```bash\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Ubuntu/Debian\nsudo apt update && sudo apt install ffmpeg\n\n# Windows (Chocolatey)\nchoco install ffmpeg\n```\n\nVerify installation:\n```bash\nffmpeg -version\n```\n\n### 2. Install Python Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n### 3. Configure API Key\n\nAPI Keys are managed by the unified `scripts/api_key.py` module, with the following retrieval priority:\n1. Alibaba Cloud CLI config `~/.aliyun/config.json` current profile's `dashscope.api_key`\n2. Environment variable `DASHSCOPE_API_KEY`\n3. Auto-create and save when Alibaba Cloud CLI is available (`generate_api_key()`)\n\n```python\n# All scripts use this unified approach\nfrom api_key import get_api_key\napi_key = get_api_key() # Returns str or None (SDK resolves auth when None)\n```\n\nManual environment variable configuration:\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support voice services) |\n| **Get Key** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\n\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\nThe `scripts/api_key.py` module creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\n\n**1. Enable AI-Mode and Update Plugins**\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n```\n\n**2. Install ModelStudio Plugin** (if not already installed)\n\n```bash\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n```\n\n**3. Disable AI-Mode After Task Completion**\n\n```bash\naliyun configure ai-mode disable\n```\n\n**CLI Commands Used**:\n\n| Command | Purpose | Called From |\n|---------|---------|------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: _delete_cloud_api_key()` |\n\n### FFmpeg Audio Processing Commands\n\n```bash\n# Query audio info\nffprobe -v error -show_entries format=format_name -show_entries stream=codec_name,sample_rate,channels -of default=noprint_wrappers=1 audio.mp3\n\n# Convert to 16kHz mono WAV (recommended for ASR)\nffmpeg -i input.mp3 -ac 1 -ar 16000 -sample_fmt s16 output.wav\n\n# Trim audio (start at 1:30, extract 2 minutes)\nffmpeg -i long_audio.wav -ss 00:01:30 -t 00:02:00 -c copy output_clip.wav\n\n# Extract audio from video\nffmpeg -i video.mp4 -vn -acodec mp3 audio.mp3\n```\n\n## Directory Structure\n\n```\nvoice-creator/\nโ”œโ”€โ”€ scripts/\nโ”‚ โ”œโ”€โ”€ api_key.py # API Key management module\nโ”‚ โ”œโ”€โ”€ speech_recognition.py # Speech recognition example\nโ”‚ โ”œโ”€โ”€ speech_synthesis.py # Speech synthesis example\nโ”‚ โ”œโ”€โ”€ generate_livestream.py # Livestream sales voice generation example\nโ”‚ โ””โ”€โ”€ requirements.txt # Python dependencies (pinned versions)\nโ”œโ”€โ”€ references/\nโ”‚ โ”œโ”€โ”€ api-docs.md # API reference documentation\nโ”‚ โ”œโ”€โ”€ models.md # Model list and selection guide\nโ”‚ โ””โ”€โ”€ error-codes.md # Error code reference\nโ”œโ”€โ”€ evals/ # Test cases\nโ”‚ โ”œโ”€โ”€ config/\nโ”‚ โ”œโ”€โ”€ scenarios/\nโ”‚ โ””โ”€โ”€ triggering/\nโ”œโ”€โ”€ related_apis.yaml\nโ””โ”€โ”€ SKILL.md\n```\n\n## Script List\n\n| Script | Function | Model |\n|--------|----------|-------|\n| `api_key.py` | API Key management (get, create, delete) | - |\n| `speech_recognition.py` | Speech recognition (long/short audio) | qwen3-asr-flash-filetrans / qwen3-asr-flash |\n| `speech_synthesis.py` | Speech synthesis (with instruction control) | qwen-tts |\n| `generate_livestream.py` | Livestream sales style voice generation | qwen-tts |\n\n**Changelog** (2026-03-18):\n- Uses `MultiModalConversation.call` API for TTS service\n- Auto-detects audio format and saves with correct extension (WAV/MP3)\n- API Key retrieval: `~/.aliyun/config.json` first, environment variable fallback\n- Clearly distinguishes DASHSCOPE_API_KEY from Coding Plan API Key\n- Added detailed Key format validation and error messages\n\n## Usage Examples\n\n### Speech Recognition\n\n```bash\npython scripts/speech_recognition.py\n```\n\n### Speech Synthesis\n\n```bash\npython scripts/speech_synthesis.py\n```\n\n### Python API Examples\n\n```python\nfrom speech_synthesis import synthesize_speech, synthesize_with_instruct\n\n# Standard synthesis\naudio_path = synthesize_speech(\n text=\"Hello, this is a test voice\",\n voice=\"Cherry\",\n output_file=\"output.wav\"\n)\n\n# Instruct-controlled synthesis (livestream sales style)\naudio_path = synthesize_with_instruct(\n text=\"Hello everyone, this product is amazing!\",\n voice=\"Cherry\",\n # NOTE: instructions must be in Chinese for the qwen-tts model\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ\",\n output_file=\"livestream.wav\"\n)\n```\n\n## Region URLs\n\n| Region | URL |\n|--------|-----|\n| Beijing | https://dashscope.aliyuncs.com/api/v1 |\n| Singapore | https://dashscope-intl.aliyuncs.com/api/v1 |\n\n**Note**: API Keys are not interchangeable between regions.\n\n## Pricing\n\n### Speech Recognition (ASR)\n\nBilled by input audio duration (seconds); output is not billed.\n\n| Model | Unit Price |\n|-------|-----------|\n| qwen3-asr-flash-filetrans | ยฅ0.00022/second |\n| qwen3-asr-flash | ยฅ0.00022/second |\n\n**Pricing Examples**:\n- 10-minute audio (600 seconds) -> ยฅ0.13\n- 1-hour audio (3600 seconds) -> ยฅ0.79\n\n### Speech Synthesis (TTS)\n\n#### qwen-tts (Token-Based)\n\nBilled by input and output tokens.\n\n| Billing Item | Unit Price |\n|-------------|-----------|\n| Input Text | ยฅ0.0016/1K tokens |\n| Output (Audio) | ยฅ0.01/1K tokens |\n\n**Pricing Examples**:\n- 100-character text -> approx. ยฅ0.0013\n- 1,000-character text -> approx. ยฅ0.013\n- 10,000-character text -> approx. ยฅ0.13\n\n**Notes**:\n- One Chinese character is approximately 1 token\n- Output tokens are calculated based on audio duration\n- View detailed bills: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Free Tier\n\nNew users receive after activating Bailian:\n- Speech Recognition: 36,000 seconds (10 hours)\n- Speech Synthesis: 10,000 characters\n- Valid for: 90 days after activation\n\n## References\n\n- [Audio File Transcription API Documentation](https://help.aliyun.com/zh/model-studio/qwen-asr-api-reference)\n- [Speech Synthesis API Documentation](https://help.aliyun.com/zh/model-studio/qwen-tts-api-reference)\n- [Model List](https://help.aliyun.com/zh/model-studio/models)\n- [Get API Key](https://help.aliyun.com/zh/model-studio/get-api-key)\n\n## Using This Skill\n\nTrigger this skill when users request tasks such as:\n- \"Convert this audio to text\"\n- \"Transcribe this recording\"\n- \"Generate a voice clip for me\"\n- \"Convert this text to speech\"\n- \"Read this text using XX voice\"\n- \"Analyze the emotions in this audio\""}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846856.440156, "n": 18468, "h": "982f03a19ebd2c18", "c": "---\nname: alibabacloud-bailian-voice-creator\ndescription: AI voice creation skill supporting speech recognition (ASR) and text-to-speech (TTS). Uses qwen3-asr-flash-filetrans, qwen-tts and other models. Use this skill when users need speech-to-text, text-to-speech, or audio processing. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Voice Creator\n\nProfessional-grade AI voice creation skill supporting speech recognition (ASR) and text-to-speech (TTS). Built on Alibaba Cloud DashScope API.\n\n## Security Rules (Mandatory)\n\n1. **Never hardcode or log API Keys in plain text** in code, logs, or output files. Patterns like `api_key = \"sk-...\"` are strictly forbidden.\n2. API Keys must only be obtained via `scripts/api_key.py`'s `get_api_key()` function, or via `os.environ.get('DASHSCOPE_API_KEY')`.\n3. Generated scripts, logs, and output files must not contain any content starting with `sk-`.\n4. Logs should only record operation status (success/failure), never credential information.\n\n## Execution Requirements (Mandatory)\n\n1. **TTS must and can only use `dashscope.MultiModalConversation.call` with the `qwen-tts` model.** Using edge-tts, gTTS, ElevenLabs, Azure TTS, sambert, NLS, or any other third-party TTS service is strictly forbidden.\n2. **Never generate mock/demo code or placeholder files.** Real API calls must be made; do not generate simulated scripts or blank audio files for any reason.\n3. **Never auto-fallback when API calls fail.** Do not switch to other TTS services after a qwen-tts failure. Catch the exception, output a clear error message, and exit.\n4. If the `dashscope` library is missing, install it first with `pip install dashscope`.\n\n## Required API Call Templates (Do Not Replace)\n\n### Standard Speech Synthesis\n\n```python\nimport dashscope\nfrom api_key import get_api_key\n\napi_key = get_api_key()\nif api_key:\n dashscope.api_key = api_key\n# If get_api_key() returns None, SDK resolves auth via environment (AK/SK, etc.)\n\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\"\n)\naudio_url = response.output.get('audio', {}).get('url', '')\n```\n\n### Instruct-Controlled Speech Synthesis (Required when user requests a specific voice style)\n\n```python\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\",\n # NOTE: instructions value must be in Chinese - the qwen-tts model processes Chinese instructions\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ\"\n)\n```\n\n**Note: The `instructions` parameter controls voice style via natural language. Do NOT substitute it with `speech_rate`, `pitch_rate`, or `volume_rate` numeric parameters.**\n\n### Error Handling Template\n\n```python\nimport sys\n\ntry:\n response = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\", text=text, voice=voice\n )\n if response.status_code != 200:\n print(f\"qwen-tts call failed: {response.code} - {response.message}\")\n sys.exit(1)\nexcept Exception as e:\n print(f\"qwen-tts call failed: {e}\")\n print(\"Please check: 1) Is DASHSCOPE_API_KEY set? 2) Is the network available?\")\n sys.exit(1)\n# Do NOT fallback to edge-tts, gTTS or other services here\n```\n\n## Feature Overview\n\n| Feature | Model | Highlights |\n|---------|-------|------------|\n| Long Audio Recognition | `qwen3-asr-flash-filetrans` | Up to 12 hours, supports emotion detection & timestamps |\n| Short Audio Recognition | `qwen3-asr-flash` | Up to 5 minutes, low latency |\n| Speech Synthesis | `qwen-tts` | Multiple voices, multilingual, instruction control |\n| Instruct-Controlled Synthesis | `qwen-tts` + instructions | Control voice expressiveness via natural language |\n\n## Orchestration Logic\n\n### Products and APIs\n\n| Product | API / SDK Call | Purpose |\n|---------|---------------|---------|\n| DashScope ASR | `Transcription.async_call` + `Transcription.wait` | Long audio recognition (async) |\n| DashScope ASR | `POST /services/audio/asr/transcription` | Short audio recognition (sync) |\n| DashScope TTS | `MultiModalConversation.call` | Speech synthesis (standard / instruct-controlled) |\n| Alibaba Cloud CLI ModelStudio | `create-api-key` / `list-workspaces` / `delete-api-key` | API Key lifecycle management |\n\n### Decision Flow\n\n```\nUser Request\n |\n +-- Intent: Audio -> Text (ASR)\n | |\n | +-- Audio duration <= 5 min AND file <= 10MB AND no emotion/timestamps needed?\n | | -> Short audio recognition: qwen3-asr-flash (sync, low latency)\n | |\n | +-- Other cases (long audio / emotion detection / timestamps needed)\n | -> Long audio recognition: qwen3-asr-flash-filetrans (async, submit + poll)\n |\n +-- Intent: Text -> Speech (TTS)\n | |\n | +-- User specified voice style/emotion/speed requirements?\n | | -> Instruct-controlled synthesis: qwen-tts + instructions parameter\n | |\n | +-- Standard reading only\n | -> Standard synthesis: qwen-tts\n |\n +-- Prerequisite: No available API Key\n -> Call api_key.py: get_api_key() auto-reads\n -> If none exists: generate_api_key() creates via Alibaba Cloud CLI and saves\n```\n\n### Call Sequence\n\n**Speech Recognition (Long Audio)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `Transcription.async_call(model, file_urls, language_hints)` -> Submit async task, get task_id\n3. `Transcription.wait(task=task_id)` -> Poll until task completes\n4. Get recognition result JSON from `output.results[].transcription_url`\n5. Parse `transcripts[].text` / `sentences[]` / `emotion` from JSON\n\n**Speech Recognition (Short Audio)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `POST /services/audio/asr/transcription` -> Sync call, returns recognized text directly\n\n**Speech Synthesis (Standard / Instruct-Controlled)**:\n1. `get_api_key()` -> Get DashScope API Key\n2. `MultiModalConversation.call(model, text, voice, [instructions])` -> Returns audio URL\n3. `download_audio(url, output_path)` -> Download audio and auto-detect format (WAV/MP3)\n\n**API Key Auto-Retrieval**:\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` -> Return if found\n2. Read environment variable `DASHSCOPE_API_KEY` -> Return if found\n3. Alibaba Cloud CLI available -> Auto-create via `generate_api_key()` and save to config\n4. All above fail -> Error with setup instructions\n\n### Quick Reference\n\n| Condition | Choice |\n|-----------|--------|\n| Audio <= 5 min and <= 10MB | `qwen3-asr-flash` |\n| Audio > 5 min or > 10MB | `qwen3-asr-flash-filetrans` |\n| Need emotion detection / timestamps / punctuation | `qwen3-asr-flash-filetrans` |\n| TTS with no style requirements | `qwen-tts` standard call |\n| TTS with style/emotion/speed requirements | `qwen-tts` + `instructions` |\n| Need dialect voices | Not supported by current `qwen-tts`; pending model update or other TTS models |\n\n## Speech Recognition (ASR) Guide\n\n### Model Selection\n\n| Scenario | Recommended Model | Notes |\n|----------|------------------|-------|\n| Meeting transcription, interview records | `qwen3-asr-flash-filetrans` | Long audio, supports emotion detection & timestamps |\n| Voice messages, real-time subtitles | `qwen3-asr-flash` | Short audio, low latency |\n| Customer service QA | `qwen3-asr-flash-filetrans` | Can analyze customer emotions |\n| Singing audio analysis | `qwen3-asr-flash-filetrans` | Supports lyrics recognition & emotion analysis |\n\n### Supported Languages\n\nChinese (Mandarin, Sichuan dialect, Minnan, Wu, Cantonese), English, Japanese, German, Korean, Russian, French, Portuguese, Arabic, Italian, Spanish, Hindi, Indonesian, Thai, Turkish, Ukrainian, Vietnamese, and 30+ other languages.\n\n### Supported Audio Formats\n\n`aac`, `amr`, `avi`, `flac`, `flv`, `m4a`, `mkv`, `mov`, `mp3`, `mp4`, `mpeg`, `ogg`, `opus`, `wav`, `webm`, `wma`, `wmv`\n\n### Feature Comparison\n\n| Feature | qwen3-asr-flash-filetrans | qwen3-asr-flash |\n|---------|---------------------------|-----------------|\n| Audio Duration | Up to 12 hours (<=2GB) | Up to 5 minutes (<=10MB) |\n| Emotion Detection | Supported (Surprise/Calm/Happy/Sad/Disgust/Angry/Fear) | Not supported |\n| Timestamps | Supported (sentence/word level) | Not supported |\n| Punctuation Prediction | Supported | Not supported |\n| Singing Recognition | Supported | Not supported |\n| Noise Rejection | Supported | Not supported |\n\n## Text-to-Speech (TTS) Guide\n\n### Model Selection\n\n| Scenario | Recommended Model | Notes |\n|----------|------------------|-------|\n| Audiobooks, radio drama dubbing | `qwen-tts` + instructions | Supports instruction control, rich expressiveness |\n| Navigation, notification announcements | `qwen-tts` | Short text, high frequency calls |\n| Online education courseware | `qwen-tts` | Multilingual support |\n\n**Important Notes**:\n- Speech synthesis uses the `MultiModalConversation.call` API\n- Audio output is in WAV format (URL valid for 24 hours)\n- The script auto-detects format and saves with the correct extension\n\n### Instruct Control (Instruct)\n\nWhen users request a specific voice style (e.g., livestream sales style, gentle style, news broadcast, etc.), **the `instructions` parameter must be used** to control voice expressiveness via natural language.\n\n**Difference between `instructions` and traditional numeric parameters**:\n- `instructions`: Natural language description, e.g., `\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…\"` -> **Must use this approach**\n- `speech_rate` / `pitch_rate` / `volume_rate`: Numeric parameters -> **Forbidden, qwen-tts does not support these parameters**\n\n**Call method** (follow strictly):\n```python\nresponse = dashscope.MultiModalConversation.call(\n model=\"qwen-tts\",\n text=\"Text to synthesize\",\n voice=\"Cherry\",\n # NOTE: instructions value must be in Chinese - the qwen-tts model processes Chinese instructions\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ๏ผŒ้Ÿณ่ฐƒๅ้ซ˜\"\n)\n```\n\n**Description dimensions reference**:\n\n| Dimension | Examples |\n|-----------|---------|\n| Pitch | High, medium, low, slightly high, slightly low |\n| Speed | Fast, medium, slow, slightly fast, slightly slow |\n| Emotion | Cheerful, calm, gentle, serious, lively, cool, healing |\n| Characteristics | Magnetic, crisp, husky, mellow, sweet, deep, powerful |\n| Use Case | News broadcast, ad voiceover, audiobook, animation character, voice assistant |\n\n**Instruction examples** (in Chinese, as required by the model):\n```\n่ฏญ้€Ÿ่พƒๅฟซ๏ผŒๅธฆๆœ‰ๆ˜Žๆ˜พ็š„ไธŠๆ‰ฌ่ฏญ่ฐƒ๏ผŒ้€‚ๅˆไป‹็ปๆ—ถๅฐšไบงๅ“\n้Ÿณ้‡็”ฑๆญฃๅธธๅฏน่ฏ่ฟ…้€Ÿๅขžๅผบ่‡ณ้ซ˜ๅ–Š๏ผŒๆ€งๆ ผ็›ด็އ๏ผŒๆƒ…็ปชๆ˜“ๆฟ€ๅŠจ\nๅ“ญ่…”ๅฏผ่‡ดๅ‘้Ÿณ็•ฅๅพฎๅซ็ณŠ๏ผŒ็•ฅๆ˜พๆฒ™ๅ“‘๏ผŒๅธฆๆœ‰ๆ˜Žๆ˜พๅ“ญ่…”็š„็ดงๅผ ๆ„Ÿ\n้Ÿณ่ฐƒๅ้ซ˜๏ผŒ่ฏญ้€Ÿไธญ็ญ‰๏ผŒๅ……ๆปกๆดปๅŠ›ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ้€‚ๅˆๅนฟๅ‘Š้…้Ÿณ\n```\n\n### Available Voices (qwen-tts)\n\nWhen calling `qwen-tts` via `MultiModalConversation.call`, the following 4 voices are supported:\n\n| voice Parameter | Voice Name | Description |\n|----------------|------------|-------------|\n| `Cherry` | Qianyue | Sunny, positive, naturally approachable young woman (Female) |\n| `Serena` | Suyao | Gentle young woman (Female) |\n| `Ethan` | Chenxu | Sunny, warm, energetic (Male) |\n| `Chelsie` | Qianxue | Anime-style virtual companion (Female) |\n\n> **Note**: Other voices (Jennifer, Ryan, Neil, Elias, and dialect voices) require the `qwen3-tts-flash` model's `SpeechSynthesizer` WebSocket API, which is not currently supported by these scripts.\n\n## Environment Setup\n\n### 1. Install FFmpeg (Audio Processing Tool)\n\nFFmpeg is used for audio format conversion, sample rate adjustment, and other preprocessing tasks.\n\n```bash\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Ubuntu/Debian\nsudo apt update && sudo apt install ffmpeg\n\n# Windows (Chocolatey)\nchoco install ffmpeg\n```\n\nVerify installation:\n```bash\nffmpeg -version\n```\n\n### 2. Install Python Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n### 3. Configure API Key\n\nAPI Keys are managed by the unified `scripts/api_key.py` module, with the following retrieval priority:\n1. Alibaba Cloud CLI config `~/.aliyun/config.json` current profile's `dashscope.api_key`\n2. Environment variable `DASHSCOPE_API_KEY`\n3. Auto-create and save when Alibaba Cloud CLI is available (`generate_api_key()`)\n\n```python\n# All scripts use this unified approach\nfrom api_key import get_api_key\napi_key = get_api_key() # Returns str or None (SDK resolves auth when None)\n```\n\nManual environment variable configuration:\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key Format** | `sk-xxx` (standard DashScope API Key) |\n| **Not Supported** | `sk-sp-xxx` (Coding Plan Key, does not support voice services) |\n| **Get Key** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\n\n### Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\nThe `scripts/api_key.py` module creates and deletes API Keys via `aliyun modelstudio` commands. Complete the following setup before use:\n\n**1. Enable AI-Mode and Update Plugins**\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n```\n\n**2. Install ModelStudio Plugin** (if not already installed)\n\n```bash\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n```\n\n**3. Disable AI-Mode After Task Completion**\n\n```bash\naliyun configure ai-mode disable\n```\n\n**CLI Commands Used**:\n\n| Command | Purpose | Called From |\n|---------|---------|------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: _delete_cloud_api_key()` |\n\n### FFmpeg Audio Processing Commands\n\n```bash\n# Query audio info\nffprobe -v error -show_entries format=format_name -show_entries stream=codec_name,sample_rate,channels -of default=noprint_wrappers=1 audio.mp3\n\n# Convert to 16kHz mono WAV (recommended for ASR)\nffmpeg -i input.mp3 -ac 1 -ar 16000 -sample_fmt s16 output.wav\n\n# Trim audio (start at 1:30, extract 2 minutes)\nffmpeg -i long_audio.wav -ss 00:01:30 -t 00:02:00 -c copy output_clip.wav\n\n# Extract audio from video\nffmpeg -i video.mp4 -vn -acodec mp3 audio.mp3\n```\n\n## Directory Structure\n\n```\nvoice-creator/\nโ”œโ”€โ”€ scripts/\nโ”‚ โ”œโ”€โ”€ api_key.py # API Key management module\nโ”‚ โ”œโ”€โ”€ speech_recognition.py # Speech recognition example\nโ”‚ โ”œโ”€โ”€ speech_synthesis.py # Speech synthesis example\nโ”‚ โ”œโ”€โ”€ generate_livestream.py # Livestream sales voice generation example\nโ”‚ โ””โ”€โ”€ requirements.txt # Python dependencies (pinned versions)\nโ”œโ”€โ”€ references/\nโ”‚ โ”œโ”€โ”€ api-docs.md # API reference documentation\nโ”‚ โ”œโ”€โ”€ models.md # Model list and selection guide\nโ”‚ โ””โ”€โ”€ error-codes.md # Error code reference\nโ”œโ”€โ”€ evals/ # Test cases\nโ”‚ โ”œโ”€โ”€ config/\nโ”‚ โ”œโ”€โ”€ scenarios/\nโ”‚ โ””โ”€โ”€ triggering/\nโ”œโ”€โ”€ related_apis.yaml\nโ””โ”€โ”€ SKILL.md\n```\n\n## Script List\n\n| Script | Function | Model |\n|--------|----------|-------|\n| `api_key.py` | API Key management (get, create, delete) | - |\n| `speech_recognition.py` | Speech recognition (long/short audio) | qwen3-asr-flash-filetrans / qwen3-asr-flash |\n| `speech_synthesis.py` | Speech synthesis (with instruction control) | qwen-tts |\n| `generate_livestream.py` | Livestream sales style voice generation | qwen-tts |\n\n**Changelog** (2026-03-18):\n- Uses `MultiModalConversation.call` API for TTS service\n- Auto-detects audio format and saves with correct extension (WAV/MP3)\n- API Key retrieval: `~/.aliyun/config.json` first, environment variable fallback\n- Clearly distinguishes DASHSCOPE_API_KEY from Coding Plan API Key\n- Added detailed Key format validation and error messages\n\n## Usage Examples\n\n### Speech Recognition\n\n```bash\npython scripts/speech_recognition.py\n```\n\n### Speech Synthesis\n\n```bash\npython scripts/speech_synthesis.py\n```\n\n### Python API Examples\n\n```python\nfrom speech_synthesis import synthesize_speech, synthesize_with_instruct\n\n# Standard synthesis\naudio_path = synthesize_speech(\n text=\"Hello, this is a test voice\",\n voice=\"Cherry\",\n output_file=\"output.wav\"\n)\n\n# Instruct-controlled synthesis (livestream sales style)\naudio_path = synthesize_with_instruct(\n text=\"Hello everyone, this product is amazing!\",\n voice=\"Cherry\",\n # NOTE: instructions must be in Chinese for the qwen-tts model\n instructions=\"่ฏญ้€Ÿๅฟซ๏ผŒๅ……ๆปก็ƒญๆƒ…ๅ’Œๆ„ŸๆŸ“ๅŠ›๏ผŒ็›ดๆ’ญๅธฆ่ดง้ฃŽๆ ผ\",\n output_file=\"livestream.wav\"\n)\n```\n\n## Region URLs\n\n| Region | URL |\n|--------|-----|\n| Beijing | https://dashscope.aliyuncs.com/api/v1 |\n| Singapore | https://dashscope-intl.aliyuncs.com/api/v1 |\n\n**Note**: API Keys are not interchangeable between regions.\n\n## Pricing\n\n### Speech Recognition (ASR)\n\nBilled by input audio duration (seconds); output is not billed.\n\n| Model | Unit Price |\n|-------|-----------|\n| qwen3-asr-flash-filetrans | ยฅ0.00022/second |\n| qwen3-asr-flash | ยฅ0.00022/second |\n\n**Pricing Examples**:\n- 10-minute audio (600 seconds) -> ยฅ0.13\n- 1-hour audio (3600 seconds) -> ยฅ0.79\n\n### Speech Synthesis (TTS)\n\n#### qwen-tts (Token-Based)\n\nBilled by input and output tokens.\n\n| Billing Item | Unit Price |\n|-------------|-----------|\n| Input Text | ยฅ0.0016/1K tokens |\n| Output (Audio) | ยฅ0.01/1K tokens |\n\n**Pricing Examples**:\n- 100-character text -> approx. ยฅ0.0013\n- 1,000-character text -> approx. ยฅ0.013\n- 10,000-character text -> approx. ยฅ0.13\n\n**Notes**:\n- One Chinese character is approximately 1 token\n- Output tokens are calculated based on audio duration\n- View detailed bills: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Free Tier\n\nNew users receive after activating Bailian:\n- Speech Recognition: 36,000 seconds (10 hours)\n- Speech Synthesis: 10,000 characters\n- Valid for: 90 days after activation\n\n## References\n\n- [Audio File Transcription API Documentation](https://help.aliyun.com/zh/model-studio/qwen-asr-api-reference)\n- [Speech Synthesis API Documentation](https://help.aliyun.com/zh/model-studio/qwen-tts-api-reference)\n- [Model List](https://help.aliyun.com/zh/model-studio/models)\n- [Get API Key](https://help.aliyun.com/zh/model-studio/get-api-key)\n\n## Using This Skill\n\nTrigger this skill when users request tasks such as:\n- \"Convert this audio to text\"\n- \"Transcribe this recording\"\n- \"Generate a voice clip for me\"\n- \"Convert this text to speech\"\n- \"Read this text using XX voice\"\n- \"Analyze the emotions in this audio\""}}, "skills/aiml/sfm/alibabacloud-bailian-image-creator/scripts/api_key.py": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674227.7591515, "n": 22228, "h": "833bddf219ee7e6a", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"Auto-installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"Warning: ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n # Check ModelStudio plugin\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n # Plugin not installed, try auto-install\n if not _install_modelstudio_plugin():\n return False\n # Verify after installation\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef _list_cloud_api_keys(workspace_id: str) -> list:\n \"\"\"\n List existing cloud API Keys via Alibaba Cloud CLI.\n\n Args:\n workspace_id: Bailian Workspace ID\n\n Returns:\n list: API Key list (each item contains apiKeyId, description, etc.)\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-api-keys\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--page-size\", \"100\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return []\n\n if result.returncode != 0:\n return []\n\n try:\n data = json.loads(result.stdout)\n return data.get(\"apiKeys\", [])\n except (json.JSONDecodeError, KeyError):\n return []\n\n\ndef generate_api_key(\n description: str = \"AI Agent auto-generated, for bailian-image-creator\",\n _retry: bool = False,\n) -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n When hitting the API Key count limit, automatically cleans up old auto-generated keys\n and retries once.\n\n Args:\n description: API Key description\n _retry: Internal flag to prevent infinite recursion\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n # Idempotent check-then-act: clean up orphan keys from failed previous attempts.\n # When generate_api_key() is called, it means no key is saved locally. If a cloud\n # key with our description already exists, it's an orphan from a timed-out previous\n # call (created on cloud but never saved locally). Delete orphans first to avoid\n # accumulating duplicate keys on retry.\n if not _retry:\n existing_keys = _list_cloud_api_keys(workspace_id)\n orphans = [k for k in existing_keys\n if description in k.get(\"description\", \"\")]\n for orphan in orphans:\n orphan_id = str(orphan.get(\"apiKeyId\", \"\"))\n if orphan_id:\n print(f\" Cleaning up orphan key from previous attempt: ID={orphan_id}\", file=sys.stderr)\n _delete_cloud_api_key(orphan_id)\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n # Handle API Key count limit: only recycle keys created by this skill, never delete others\n if not _retry and (\"Limit\" in stderr or \"limit\" in stderr.lower()):\n print(\"Warning: API Key count has reached the limit. Attempting to recycle old keys from this skill...\", file=sys.stderr)\n existing_keys = _list_cloud_api_keys(workspace_id)\n # Only recycle keys created by this skill (matched by description), never touch other keys\n recyclable = [k for k in existing_keys\n if description in k.get(\"description\", \"\")]\n if recyclable:\n old_key_id = str(recyclable[0].get(\"apiKeyId\", \"\"))\n old_key_desc = recyclable[0].get(\"description\", \"\")\n if old_key_id:\n print(f\" Found recyclable old key: ID={old_key_id}, description='{old_key_desc}'\", file=sys.stderr)\n delete_result = _delete_cloud_api_key(old_key_id)\n if delete_result[\"success\"]:\n print(f\"Old key recycled (ID: {old_key_id}). Recreating...\", file=sys.stderr)\n return generate_api_key(description, _retry=True)\n raise RuntimeError(\n \"API Key count has reached account limit and no recyclable keys from this skill were found\\n\\n\"\n \"Please manually delete unused API Keys:\\n\"\n \" https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:3]}***\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (with protective pre-checks).\n\n Pre-check rules:\n - No api_key_id (manually configured key): only remove local ref, skip cloud deletion\n - Has api_key_id: query cloud to confirm ownership by this skill before cloud deletion\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # Protective pre-check: only delete cloud keys auto-created by this skill\n if api_key_id:\n # Has api_key_id, possibly auto-created; verify cloud description to confirm\n skill_description = \"bailian-image-creator\"\n should_delete_cloud = False\n try:\n workspace_id = _get_workspace_id()\n existing_keys = _list_cloud_api_keys(workspace_id)\n matched = [k for k in existing_keys\n if str(k.get(\"apiKeyId\", \"\")) == str(api_key_id)]\n if matched:\n desc = matched[0].get(\"description\", \"\")\n if skill_description in desc:\n should_delete_cloud = True\n else:\n print(f\"Warning: Cloud key (ID={api_key_id}) description '{desc}' \"\n f\"does not match skill identifier '{skill_description}', \"\n f\"skipping cloud deletion\",\n file=sys.stderr)\n messages.append(\"Cloud key description does not match this skill, \"\n \"skipping cloud deletion (safety)\")\n else:\n messages.append(f\"Cloud key ID={api_key_id} not found, skipping cloud deletion\")\n except RuntimeError as e:\n messages.append(f\"Cannot query cloud key status: {e}, skipping cloud deletion\")\n\n if should_delete_cloud:\n print(f\"Confirmed cloud key (ID={api_key_id}) was auto-created by this skill, deleting...\",\n file=sys.stderr)\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n # No api_key_id means manually configured key, possibly shared by other services\n print(\"Warning: Key has no api_key_id (likely manually configured), \"\n \"only removing local reference, skipping cloud deletion\",\n file=sys.stderr)\n messages.append(\"Key is manually configured, only removing local reference, \"\n \"cloud key not deleted\")\n\n # Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support image generation\\n\"\n \" - Image generation requires a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846881.7456613, "n": 22228, "h": "833bddf219ee7e6a", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"Auto-installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"Warning: ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n # Check ModelStudio plugin\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n # Plugin not installed, try auto-install\n if not _install_modelstudio_plugin():\n return False\n # Verify after installation\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef _list_cloud_api_keys(workspace_id: str) -> list:\n \"\"\"\n List existing cloud API Keys via Alibaba Cloud CLI.\n\n Args:\n workspace_id: Bailian Workspace ID\n\n Returns:\n list: API Key list (each item contains apiKeyId, description, etc.)\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-api-keys\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--page-size\", \"100\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return []\n\n if result.returncode != 0:\n return []\n\n try:\n data = json.loads(result.stdout)\n return data.get(\"apiKeys\", [])\n except (json.JSONDecodeError, KeyError):\n return []\n\n\ndef generate_api_key(\n description: str = \"AI Agent auto-generated, for bailian-image-creator\",\n _retry: bool = False,\n) -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n When hitting the API Key count limit, automatically cleans up old auto-generated keys\n and retries once.\n\n Args:\n description: API Key description\n _retry: Internal flag to prevent infinite recursion\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n # Idempotent check-then-act: clean up orphan keys from failed previous attempts.\n # When generate_api_key() is called, it means no key is saved locally. If a cloud\n # key with our description already exists, it's an orphan from a timed-out previous\n # call (created on cloud but never saved locally). Delete orphans first to avoid\n # accumulating duplicate keys on retry.\n if not _retry:\n existing_keys = _list_cloud_api_keys(workspace_id)\n orphans = [k for k in existing_keys\n if description in k.get(\"description\", \"\")]\n for orphan in orphans:\n orphan_id = str(orphan.get(\"apiKeyId\", \"\"))\n if orphan_id:\n print(f\" Cleaning up orphan key from previous attempt: ID={orphan_id}\", file=sys.stderr)\n _delete_cloud_api_key(orphan_id)\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n # Handle API Key count limit: only recycle keys created by this skill, never delete others\n if not _retry and (\"Limit\" in stderr or \"limit\" in stderr.lower()):\n print(\"Warning: API Key count has reached the limit. Attempting to recycle old keys from this skill...\", file=sys.stderr)\n existing_keys = _list_cloud_api_keys(workspace_id)\n # Only recycle keys created by this skill (matched by description), never touch other keys\n recyclable = [k for k in existing_keys\n if description in k.get(\"description\", \"\")]\n if recyclable:\n old_key_id = str(recyclable[0].get(\"apiKeyId\", \"\"))\n old_key_desc = recyclable[0].get(\"description\", \"\")\n if old_key_id:\n print(f\" Found recyclable old key: ID={old_key_id}, description='{old_key_desc}'\", file=sys.stderr)\n delete_result = _delete_cloud_api_key(old_key_id)\n if delete_result[\"success\"]:\n print(f\"Old key recycled (ID: {old_key_id}). Recreating...\", file=sys.stderr)\n return generate_api_key(description, _retry=True)\n raise RuntimeError(\n \"API Key count has reached account limit and no recyclable keys from this skill were found\\n\\n\"\n \"Please manually delete unused API Keys:\\n\"\n \" https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:3]}***\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (with protective pre-checks).\n\n Pre-check rules:\n - No api_key_id (manually configured key): only remove local ref, skip cloud deletion\n - Has api_key_id: query cloud to confirm ownership by this skill before cloud deletion\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # Protective pre-check: only delete cloud keys auto-created by this skill\n if api_key_id:\n # Has api_key_id, possibly auto-created; verify cloud description to confirm\n skill_description = \"bailian-image-creator\"\n should_delete_cloud = False\n try:\n workspace_id = _get_workspace_id()\n existing_keys = _list_cloud_api_keys(workspace_id)\n matched = [k for k in existing_keys\n if str(k.get(\"apiKeyId\", \"\")) == str(api_key_id)]\n if matched:\n desc = matched[0].get(\"description\", \"\")\n if skill_description in desc:\n should_delete_cloud = True\n else:\n print(f\"Warning: Cloud key (ID={api_key_id}) description '{desc}' \"\n f\"does not match skill identifier '{skill_description}', \"\n f\"skipping cloud deletion\",\n file=sys.stderr)\n messages.append(\"Cloud key description does not match this skill, \"\n \"skipping cloud deletion (safety)\")\n else:\n messages.append(f\"Cloud key ID={api_key_id} not found, skipping cloud deletion\")\n except RuntimeError as e:\n messages.append(f\"Cannot query cloud key status: {e}, skipping cloud deletion\")\n\n if should_delete_cloud:\n print(f\"Confirmed cloud key (ID={api_key_id}) was auto-created by this skill, deleting...\",\n file=sys.stderr)\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n # No api_key_id means manually configured key, possibly shared by other services\n print(\"Warning: Key has no api_key_id (likely manually configured), \"\n \"only removing local reference, skipping cloud deletion\",\n file=sys.stderr)\n messages.append(\"Key is manually configured, only removing local reference, \"\n \"cloud key not deleted\")\n\n # Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support image generation\\n\"\n \" - Image generation requires a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key\n"}}, "skills/aiml/sfm/alibabacloud-bailian-video-creator/SKILL.md": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674227.8709126, "n": 19484, "h": "f88178661c8cde73", "c": "---\nname: alibabacloud-bailian-video-creator\ndescription: AI video creation skill supporting text-to-video, image-to-video, reference-to-video, video editing, and video understanding. Uses wan2.6-t2v, wan2.6-i2v, wan2.6-r2v-flash, wanx2.1-vace-plus, qwen3.5-plus and other models. Use this skill when users need to generate videos, edit videos, or analyze video content. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Video Creator\n\nProfessional-grade AI video creation skill supporting text-to-video, image-to-video, video editing, and video understanding. Built on Alibaba Cloud DashScope API.\n\n## Prompt Design Guide\n\nThe core formula for high-quality video prompts: `[Scene Overview] + [Shot Design] + [Camera Movement] + [Lighting & Atmosphere] + [Visual Style]`\n\nKey elements: Camera language (shot size, movement, angle), shot rhythm design, lighting and atmosphere control, visual style reference. For multi-shot videos, use the shot list format `Shot N [start-end time] Shot description`.\n\nNegative prompt template: `blurry, low quality, distorted, glitchy, unnatural movement, jumpy cuts, inconsistent lighting, artifacts, watermark, text overlay, static, frozen frames, uncanny valley`\n\n> For detailed camera language reference tables, shot list templates, lighting styles, and complete prompt examples, refer to [references/prompt-guide.md](references/prompt-guide.md)\n\n## Version\n\nv2.1.0 (2026-03-10)\n\n## Feature List\n\n| Feature | Model | Status | Description |\n|---------|-------|--------|-------------|\n| Video Understanding | qwen3.5-plus | โœ… | Analyze video content with configurable frame extraction rate |\n| Text-to-Video (Multi-shot) | wan2.6-t2v | โœ… | Generate multi-shot videos from text descriptions |\n| Image-to-Video | wan2.6-i2v | โœ… | Generate video from image + audio |\n| Reference-to-Video | wan2.6-r2v-flash | โœ… | Generate multi-character video from multiple reference materials (video/image) |\n| Video Editing (Repainting) | wanx2.1-vace-plus | โœ… | Video repainting while preserving original motion/structure |\n| Video Region Edit | wanx2.1-vace-plus | โœ… | Fine-grained editing of specific video regions |\n\n**Note**: The DashScope qwen3.5-plus model's `image_url` type only supports static image analysis and does not support direct video file upload. To analyze video, extract key frames first and use the image analysis feature.\n\n## Prerequisites\n\nBefore executing any task, the following checks must be completed:\n\n1. **Verify API Key availability**: Run `from api_key import get_api_key; api_key = get_api_key()`. If `get_api_key()` raises an exception (including \"quota exceeded\", `AuthenticationError`, `InvalidApiKey`, `401`, etc.), **you must immediately stop execution and display the error message to the user** -- do not skip API calls, do not continue with subsequent steps, do not fabricate execution results\n2. **Confirm remote API availability**: This skill completes all video generation and content analysis tasks via the DashScope remote API, without relying on local GPU, PyTorch, Stable Diffusion, etc. As long as the API Key is valid, the skill has full capabilities. **Do not claim inability to complete tasks just because local tools are missing**\n3. **API Key security**: Never write the full API Key in plain text in script files, logs, or terminal output. Must read via the `api_key.py` module or environment variable `os.environ.get(\"DASHSCOPE_API_KEY\")`. Display in masked form when debugging (e.g., `sk-***xxx`)\n\n## Orchestration Logic\n\n### Products and Services Involved\n\nAll APIs in this skill are called through **Alibaba Cloud DashScope (Bailian)**; no other cloud products are involved.\n\n| Component | Purpose |\n|-----------|---------|\n| `scripts/api_key.py` | Unified API Key management (reads from `~/.aliyun/config.json` or environment variable) |\n| DashScope Video Generation API | Text-to-video, image-to-video, reference-to-video, video editing (async) |\n| DashScope MultiModalConversation API | Video understanding (sync) |\n| FFmpeg (local tool) | Video format conversion, key frame extraction, and other pre/post-processing (optional) |\n\n### Feature Selection Decision Tree\n\nSelect the corresponding feature based on the user's **input materials** and **intent**:\n\n```\nUser Request\nโ”‚\nโ”œโ”€ User has an existing video to analyze/understand?\nโ”‚ โ””โ”€ YES โ†’ Video Understanding (video_understanding.py, qwen3.5-plus)\nโ”‚\nโ”œโ”€ User has an existing video to modify?\nโ”‚ โ”œโ”€ Modify a local region (has mask)?\nโ”‚ โ”‚ โ””โ”€ YES โ†’ Video Region Edit (video_local_edit.py, wanx2.1-vace-plus)\nโ”‚ โ””โ”€ Overall style repainting?\nโ”‚ โ””โ”€ YES โ†’ Video Editing (video_edit.py, wanx2.1-vace-plus)\nโ”‚\nโ”œโ”€ User has multiple reference materials (character videos/images) to synthesize?\nโ”‚ โ””โ”€ YES โ†’ Reference-to-Video (reference_to_video.py, wan2.6-r2v-flash)\nโ”‚\nโ”œโ”€ User has a single image to generate video from?\nโ”‚ โ””โ”€ YES โ†’ Image-to-Video (image_to_video.py, wan2.6-i2v)\nโ”‚\nโ””โ”€ User only has a text description?\n โ””โ”€ YES โ†’ Text-to-Video (text_to_video.py, wan2.6-t2v)\n```\n\n### Quick Reference Table\n\n| User Input | Feature | Script | Model |\n|------------|---------|--------|-------|\n| Video URL + analysis question | Video Understanding | `video_understanding.py` | qwen3.5-plus |\n| Pure text description | Text-to-Video | `text_to_video.py` | wan2.6-t2v |\n| Image URL (ยฑ audio URL) | Image-to-Video | `image_to_video.py` | wan2.6-i2v |\n| Multiple reference materials (video/image mix) | Reference-to-Video | `reference_to_video.py` | wan2.6-r2v-flash |\n| Video URL + new style description | Video Editing | `video_edit.py` | wanx2.1-vace-plus |\n| Video URL + mask image + edit description | Video Region Edit | `video_local_edit.py` | wanx2.1-vace-plus |\n\n### Invocation Flow\n\n**You must directly run existing scripts in the `scripts/` directory to complete tasks. Creating new scripts from scratch as replacements is forbidden.** Each feature has a corresponding executable script (see Quick Reference Table). Simply pass the correct parameters and run. The scripts internally encapsulate the complete flow of API Key retrieval, API calls, polling, and result output.\n\nAll features share a unified invocation pattern, divided into two categories:\n\n**Synchronous call (Video Understanding)**:\n```python\nfrom dashscope import MultiModalConversation\nresponse = MultiModalConversation.call(model=\"qwen3.5-plus\", messages=[...])\n# Returns analysis result directly\n```\n\n**Asynchronous call (All video generation/editing)**:\n\nStep 1 -- Create task (obtain task_id):\n```python\nresponse = requests.post(\n \"https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis\",\n headers={\"Authorization\": f\"Bearer {api_key}\", \"X-DashScope-Async\": \"enable\"},\n json={\"model\": \"wan2.6-t2v\", \"input\": {\"prompt\": prompt}, \"parameters\": {...}}\n)\ntask_id = response.json()[\"output\"][\"task_id\"]\n```\n\nStep 2 -- Poll task status (until SUCCEEDED or FAILED):\n```python\nimport time\nfor _ in range(60): # Poll up to 60 times, 10-second intervals\n result = requests.get(\n f\"https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}\",\n headers={\"Authorization\": f\"Bearer {api_key}\"}\n ).json()\n status = result[\"output\"][\"task_status\"]\n if status == \"SUCCEEDED\":\n video_url = result[\"output\"][\"video_url\"]\n break\n elif status == \"FAILED\":\n raise RuntimeError(result[\"output\"].get(\"message\", \"Task failed\"))\n time.sleep(10)\n```\n\nStep 3 -- Verify result: Confirm that a valid `video_url` or analysis result has been obtained. Do not end execution prematurely before the task completes.\n\n### Key Orchestration Rules\n\n1. **API Key retrieval must come first**: All scripts call `get_api_key()` at module load time. If no valid Key is found, an error is raised immediately without making API requests\n2. **Async tasks require polling**: Video generation typically takes 1-5 minutes; you must poll via `poll_result()` and wait for completion\n3. **Prompt optimization**: For text-to-video and image-to-video, setting `prompt_extend=True` is recommended to let the model auto-rewrite prompts; for video editing/region edit, set to `False` to avoid rewrites deviating from original intent\n4. **Video Editing vs Region Edit selection**:\n - User provided a mask image โ†’ Use Region Edit\n - User only described a new style without mask โ†’ Use Video Editing (Repainting)\n5. **control_condition selection (Video Editing)**:\n - Need to preserve facial expressions + body movements โ†’ `posebodyface`\n - Only preserve body movements, change face โ†’ `posebody`\n - Preserve composition and motion trajectories โ†’ `depth` (default, most versatile)\n - Preserve line art structure โ†’ `scribble`\n\n### Constraints\n\n**All tasks must be completed via the DashScope remote API. Using local tools (OpenCV, PyTorch, Stable Diffusion, FFmpeg analysis, etc.) to replace DashScope API for video generation or content analysis is forbidden. Using third-party alternative services (such as Pollinations.ai, RunwayML, Pika, Luma, etc.) to replace DashScope API is forbidden. Claiming \"no video generation capability\" or \"missing tools\" is forbidden -- as long as the API Key is valid, this skill has full remote video generation and understanding capabilities.**\n\n1. **API Key pre-check**: Before any API call, you must verify the API Key is configured via `get_api_key()`. If `get_api_key()` fails (including \"quota exceeded\", `401`, `InvalidApiKey`, etc.), you must **immediately stop and report the error** -- do not skip API calls, do not fabricate subsequent results\n2. **Correct model names** (no other names may be used):\n\n| Feature | Required Model | Incorrect Examples (Forbidden) |\n|---------|---------------|-------------------------------|\n| Text-to-Video | `wan2.6-t2v` | `wanx2.6-t2v`, `wanx2.1-t2v-plus`, `wanx-video-generation-v1` |\n| Image-to-Video (HTTP API) | `wan2.6-i2v` | - |\n| Image-to-Video (SDK) | `wan2.6-i2v-flash` | - |\n| Reference-to-Video | `wan2.6-r2v-flash` | - |\n| Video Editing/Region Edit | `wanx2.1-vace-plus` | - |\n| Video Understanding | `qwen3.5-plus` | `qwen-vl-plus`, `qwen-vl-max-latest` |\n\n3. **Async tasks must be polled to completion**: After creating a task, you must poll `task_status` until `SUCCEEDED` or `FAILED`. Do not end prematurely before the task completes. If task creation fails (e.g., 401 error), you must report the error clearly and prompt the user to check API Key -- do not silently terminate\n4. **Final result must be verified**: Confirm that a valid `video_url` (video generation) or analysis text (video understanding) has been obtained. Do not return empty results. **If the API call failed or was not actually executed, you must honestly report the error. Fabricating analysis results or fictitious video URLs is forbidden**\n5. **API Key security**: Never write the full API Key in plain text in generated scripts, logs, or terminal commands. Specific rules:\n\n โŒ **Forbidden** (none of the following are allowed):\n ```python\n api_key = \"sk-abc123...\" # Hardcoding forbidden\n headers = {\"Authorization\": \"Bearer sk-abc123...\"} # Plain text forbidden\n ```\n ```bash\n export DASHSCOPE_API_KEY=\"sk-abc123...\" # Plain text export in scripts forbidden\n ```\n\n โœ… **Correct** (must use the following methods):\n ```python\n from api_key import get_api_key\n api_key = get_api_key() # Securely obtained via module\n # or\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\") # Obtained via environment variable\n ```\n\n6. **No fabricating results**: If the DashScope API call was not actually executed or execution failed, you must clearly inform the user of the failure reason. Do not fabricate video analysis text, fictitious video URLs, or claim task completion when api.json is empty. **Writing a script containing API call code โ‰  actually executing the API call. The standard: only when the script is actually run and the DashScope API (e.g., `MultiModalConversation.call` or `dashscope.aliyuncs.com/api/v1/...`) returns a real result can you claim task completion. API Key management calls (ListWorkspaces, CreateApiKey) do not count as task completion**\n7. **Third-party alternative services forbidden**: All video generation and understanding tasks must be completed via DashScope API. Calling Pollinations.ai, RunwayML, Pika, Luma, Kling, or other third-party services as alternatives is forbidden\n8. **Output file verification**: Before claiming task completion, you must confirm that actual output files have been written to disk (e.g., `video_analysis_result.txt`, downloaded video files, etc.). If the file does not exist or is empty, you must report failure\n9. **Must use existing scripts and actually execute them**: Use existing scripts in the `scripts/` directory (e.g., `python scripts/video_understanding.py`). Creating new scripts from scratch as replacements is forbidden. Scripts must be actually executed (`python scripts/xxx.py`), and real stdout/stderr output must be captured as the basis for results. If script execution encounters errors, you must display the real error message\n\n## Environment Setup\n\n### 1. Install FFmpeg (Video Processing Tool)\n\nFFmpeg is used for video format conversion, key frame extraction, video trimming/concatenation, and other local processing.\n\n```bash\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Ubuntu/Debian\nsudo apt update && sudo apt install ffmpeg\n\n# Windows (Chocolatey)\nchoco install ffmpeg\n\n# Windows (Scoop)\nscoop install ffmpeg\n```\n\nVerify installation:\n```bash\nffmpeg -version\n```\n\n### 2. Install Python Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n| Package | Version Constraint | Purpose |\n|---------|-------------------|---------|\n| `dashscope` | `>=1.25.0,<2.0.0` | SDK-based image-to-video, video understanding |\n| `requests` | `>=2.20.0,<3.0.0` | HTTP API-based video generation/editing |\n\n### 3. Configure API Key\n\nAPI Key is managed through the unified `scripts/api_key.py` module with three-tier retrieval logic:\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` โ†’ return if found\n2. Read environment variable `DASHSCOPE_API_KEY` โ†’ return if found\n3. Detect Alibaba Cloud CLI available โ†’ auto-call `generate_api_key()` to create Key and save to config โ†’ return\n4. All fail โ†’ raise error (with CLI installation instructions)\n\n```python\n# All scripts use this unified approach\nfrom api_key import get_api_key\napi_key = get_api_key()\n```\n\nManual environment variable configuration:\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key format** | `sk-xxx` (standard DashScope API Key) |\n| **Not usable** | `sk-sp-xxx` (Coding Plan exclusive Key, does not support video generation) |\n| **Obtain at** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\n\n### 4. Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\nThis skill's `scripts/api_key.py` uses `aliyun modelstudio` commands to automatically create and delete API Keys. Complete the following configuration before use:\n\n**1. Enable AI-Mode and update plugins**\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n```\n\n**2. Install ModelStudio plugin** (if not already installed)\n\n```bash\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n```\n\n**3. Disable AI-Mode after task completion**\n\n```bash\naliyun configure ai-mode disable\n```\n\n**CLI commands involved**:\n\n| Command | Purpose | Call Location |\n|---------|---------|--------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: delete_api_key()` |\n\n## Directory Structure\n\n```\nalibabacloud-bailian-video-creator/\nโ”œโ”€โ”€ scripts/ # Example code (7 core features + API Key management)\nโ”‚ โ”œโ”€โ”€ requirements.txt\nโ”‚ โ”œโ”€โ”€ api_key.py\nโ”‚ โ”œโ”€โ”€ video_understanding.py\nโ”‚ โ”œโ”€โ”€ text_to_video.py\nโ”‚ โ”œโ”€โ”€ image_to_video.py\nโ”‚ โ”œโ”€โ”€ image_to_video_sdk.py\nโ”‚ โ”œโ”€โ”€ reference_to_video.py\nโ”‚ โ”œโ”€โ”€ video_edit.py\nโ”‚ โ””โ”€โ”€ video_local_edit.py\nโ”œโ”€โ”€ references/ # Reference materials\nโ”‚ โ”œโ”€โ”€ prompt-guide.md\nโ”‚ โ”œโ”€โ”€ api-docs.md\nโ”‚ โ”œโ”€โ”€ models.md\nโ”‚ โ””โ”€โ”€ error-codes.md\nโ””โ”€โ”€ SKILL.md\n```\n\n**Notes**:\n- `scripts/` - Contains only official example code, one file per feature\n- `references/` - Prompt guide, API documentation, model list, error codes, and other reference materials\n\n## Usage Examples\n\nRun any script directly to see detailed parameter descriptions (`python scripts/.py`).\n\n| Feature | Script | Key Parameters |\n|---------|--------|---------------|\n| Video Understanding | `video_understanding.py` | `video_url`, `prompt`, `fps` |\n| Text-to-Video | `text_to_video.py` | `prompt` (with shot list), `duration`, `size` |\n| Image-to-Video (HTTP) | `image_to_video.py` | `prompt`, `img_url`, `audio_url`, `resolution` |\n| Image-to-Video (SDK) | `image_to_video_sdk.py` | `prompt`, `img_url`, `audio_url`, `duration` |\n| Reference-to-Video | `reference_to_video.py` | `prompt` (with Character references), `reference_urls` |\n| Video Editing | `video_edit.py` | `prompt`, `video_url`, `control_condition` |\n| Video Region Edit | `video_local_edit.py` | `prompt`, `video_url`, `mask_image_url`, `mask_type` |\n\n## Region URLs\n\nAPI URLs differ by region. Choose based on your actual needs:\n\n| Region | URL |\n|--------|-----|\n| Beijing | https://dashscope.aliyuncs.com/api/v1 |\n| Virginia | https://dashscope-us.aliyuncs.com/api/v1 |\n| Singapore | https://dashscope-intl.aliyuncs.com/api/v1 |\n\n## Billing\n\n### Video Generation Billing\n\nBilled by the duration (seconds) of successfully generated videos. Failed requests are not billed.\n\n#### Text-to-Video (wan2.6-t2v)\n\n| Resolution | Unit Price |\n|------------|-----------|\n| 720P | ยฅ0.6/sec |\n| 1080P | ยฅ1.0/sec |\n\n**Billing examples**:\n- Generate 10-second 720P video โ†’ ยฅ6.00\n- Generate 10-second 1080P video โ†’ ยฅ10.00\n\n#### Image-to-Video / Reference-to-Video (wan2.6-i2v-flash / wan2.6-r2v-flash)\n\n| Resolution | Video Type | Unit Price |\n|------------|-----------|-----------|\n| 720P | With audio | ยฅ0.3/sec |\n| 720P | Silent | ยฅ0.15/sec |\n| 1080P | With audio | ยฅ0.5/sec |\n| 1080P | Silent | ยฅ0.25/sec |\n\n**Note**: Reference-to-video is billed by input + output total duration. Input video is billed for a maximum of 5 seconds.\n\n#### Video Editing (wanx2.1-vace-plus)\n\n| Resolution | Unit Price |\n|------------|-----------|\n| 720P | ยฅ0.72/sec |\n\n**View billing**: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Free Quota\n\nNew users receive upon activating Bailian:\n- 50 seconds of free video quota\n- Valid for: 90 days after activation\n\n## References\n\n- [Text-to-Video API Documentation](https://help.aliyun.com/zh/model-studio/text-to-video-api-reference)\n- [Image-to-Video API Documentation](https://help.aliyun.com/zh/model-studio/image-to-video-api-reference)\n- [Video Editing API Documentation](https://help.aliyun.com/zh/model-studio/wanx-vace-api-reference)\n- [Model List](https://help.aliyun.com/zh/model-studio/models)\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846881.273038, "n": 19484, "h": "f88178661c8cde73", "c": "---\nname: alibabacloud-bailian-video-creator\ndescription: AI video creation skill supporting text-to-video, image-to-video, reference-to-video, video editing, and video understanding. Uses wan2.6-t2v, wan2.6-i2v, wan2.6-r2v-flash, wanx2.1-vace-plus, qwen3.5-plus and other models. Use this skill when users need to generate videos, edit videos, or analyze video content. Note: on first run, it will auto-manage DashScope API Keys (create/recycle) and may auto-install the Alibaba Cloud CLI ModelStudio plugin.\n---\n\n# AI Video Creator\n\nProfessional-grade AI video creation skill supporting text-to-video, image-to-video, video editing, and video understanding. Built on Alibaba Cloud DashScope API.\n\n## Prompt Design Guide\n\nThe core formula for high-quality video prompts: `[Scene Overview] + [Shot Design] + [Camera Movement] + [Lighting & Atmosphere] + [Visual Style]`\n\nKey elements: Camera language (shot size, movement, angle), shot rhythm design, lighting and atmosphere control, visual style reference. For multi-shot videos, use the shot list format `Shot N [start-end time] Shot description`.\n\nNegative prompt template: `blurry, low quality, distorted, glitchy, unnatural movement, jumpy cuts, inconsistent lighting, artifacts, watermark, text overlay, static, frozen frames, uncanny valley`\n\n> For detailed camera language reference tables, shot list templates, lighting styles, and complete prompt examples, refer to [references/prompt-guide.md](references/prompt-guide.md)\n\n## Version\n\nv2.1.0 (2026-03-10)\n\n## Feature List\n\n| Feature | Model | Status | Description |\n|---------|-------|--------|-------------|\n| Video Understanding | qwen3.5-plus | โœ… | Analyze video content with configurable frame extraction rate |\n| Text-to-Video (Multi-shot) | wan2.6-t2v | โœ… | Generate multi-shot videos from text descriptions |\n| Image-to-Video | wan2.6-i2v | โœ… | Generate video from image + audio |\n| Reference-to-Video | wan2.6-r2v-flash | โœ… | Generate multi-character video from multiple reference materials (video/image) |\n| Video Editing (Repainting) | wanx2.1-vace-plus | โœ… | Video repainting while preserving original motion/structure |\n| Video Region Edit | wanx2.1-vace-plus | โœ… | Fine-grained editing of specific video regions |\n\n**Note**: The DashScope qwen3.5-plus model's `image_url` type only supports static image analysis and does not support direct video file upload. To analyze video, extract key frames first and use the image analysis feature.\n\n## Prerequisites\n\nBefore executing any task, the following checks must be completed:\n\n1. **Verify API Key availability**: Run `from api_key import get_api_key; api_key = get_api_key()`. If `get_api_key()` raises an exception (including \"quota exceeded\", `AuthenticationError`, `InvalidApiKey`, `401`, etc.), **you must immediately stop execution and display the error message to the user** -- do not skip API calls, do not continue with subsequent steps, do not fabricate execution results\n2. **Confirm remote API availability**: This skill completes all video generation and content analysis tasks via the DashScope remote API, without relying on local GPU, PyTorch, Stable Diffusion, etc. As long as the API Key is valid, the skill has full capabilities. **Do not claim inability to complete tasks just because local tools are missing**\n3. **API Key security**: Never write the full API Key in plain text in script files, logs, or terminal output. Must read via the `api_key.py` module or environment variable `os.environ.get(\"DASHSCOPE_API_KEY\")`. Display in masked form when debugging (e.g., `sk-***xxx`)\n\n## Orchestration Logic\n\n### Products and Services Involved\n\nAll APIs in this skill are called through **Alibaba Cloud DashScope (Bailian)**; no other cloud products are involved.\n\n| Component | Purpose |\n|-----------|---------|\n| `scripts/api_key.py` | Unified API Key management (reads from `~/.aliyun/config.json` or environment variable) |\n| DashScope Video Generation API | Text-to-video, image-to-video, reference-to-video, video editing (async) |\n| DashScope MultiModalConversation API | Video understanding (sync) |\n| FFmpeg (local tool) | Video format conversion, key frame extraction, and other pre/post-processing (optional) |\n\n### Feature Selection Decision Tree\n\nSelect the corresponding feature based on the user's **input materials** and **intent**:\n\n```\nUser Request\nโ”‚\nโ”œโ”€ User has an existing video to analyze/understand?\nโ”‚ โ””โ”€ YES โ†’ Video Understanding (video_understanding.py, qwen3.5-plus)\nโ”‚\nโ”œโ”€ User has an existing video to modify?\nโ”‚ โ”œโ”€ Modify a local region (has mask)?\nโ”‚ โ”‚ โ””โ”€ YES โ†’ Video Region Edit (video_local_edit.py, wanx2.1-vace-plus)\nโ”‚ โ””โ”€ Overall style repainting?\nโ”‚ โ””โ”€ YES โ†’ Video Editing (video_edit.py, wanx2.1-vace-plus)\nโ”‚\nโ”œโ”€ User has multiple reference materials (character videos/images) to synthesize?\nโ”‚ โ””โ”€ YES โ†’ Reference-to-Video (reference_to_video.py, wan2.6-r2v-flash)\nโ”‚\nโ”œโ”€ User has a single image to generate video from?\nโ”‚ โ””โ”€ YES โ†’ Image-to-Video (image_to_video.py, wan2.6-i2v)\nโ”‚\nโ””โ”€ User only has a text description?\n โ””โ”€ YES โ†’ Text-to-Video (text_to_video.py, wan2.6-t2v)\n```\n\n### Quick Reference Table\n\n| User Input | Feature | Script | Model |\n|------------|---------|--------|-------|\n| Video URL + analysis question | Video Understanding | `video_understanding.py` | qwen3.5-plus |\n| Pure text description | Text-to-Video | `text_to_video.py` | wan2.6-t2v |\n| Image URL (ยฑ audio URL) | Image-to-Video | `image_to_video.py` | wan2.6-i2v |\n| Multiple reference materials (video/image mix) | Reference-to-Video | `reference_to_video.py` | wan2.6-r2v-flash |\n| Video URL + new style description | Video Editing | `video_edit.py` | wanx2.1-vace-plus |\n| Video URL + mask image + edit description | Video Region Edit | `video_local_edit.py` | wanx2.1-vace-plus |\n\n### Invocation Flow\n\n**You must directly run existing scripts in the `scripts/` directory to complete tasks. Creating new scripts from scratch as replacements is forbidden.** Each feature has a corresponding executable script (see Quick Reference Table). Simply pass the correct parameters and run. The scripts internally encapsulate the complete flow of API Key retrieval, API calls, polling, and result output.\n\nAll features share a unified invocation pattern, divided into two categories:\n\n**Synchronous call (Video Understanding)**:\n```python\nfrom dashscope import MultiModalConversation\nresponse = MultiModalConversation.call(model=\"qwen3.5-plus\", messages=[...])\n# Returns analysis result directly\n```\n\n**Asynchronous call (All video generation/editing)**:\n\nStep 1 -- Create task (obtain task_id):\n```python\nresponse = requests.post(\n \"https://dashscope.aliyuncs.com/api/v1/services/aigc/video-generation/video-synthesis\",\n headers={\"Authorization\": f\"Bearer {api_key}\", \"X-DashScope-Async\": \"enable\"},\n json={\"model\": \"wan2.6-t2v\", \"input\": {\"prompt\": prompt}, \"parameters\": {...}}\n)\ntask_id = response.json()[\"output\"][\"task_id\"]\n```\n\nStep 2 -- Poll task status (until SUCCEEDED or FAILED):\n```python\nimport time\nfor _ in range(60): # Poll up to 60 times, 10-second intervals\n result = requests.get(\n f\"https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}\",\n headers={\"Authorization\": f\"Bearer {api_key}\"}\n ).json()\n status = result[\"output\"][\"task_status\"]\n if status == \"SUCCEEDED\":\n video_url = result[\"output\"][\"video_url\"]\n break\n elif status == \"FAILED\":\n raise RuntimeError(result[\"output\"].get(\"message\", \"Task failed\"))\n time.sleep(10)\n```\n\nStep 3 -- Verify result: Confirm that a valid `video_url` or analysis result has been obtained. Do not end execution prematurely before the task completes.\n\n### Key Orchestration Rules\n\n1. **API Key retrieval must come first**: All scripts call `get_api_key()` at module load time. If no valid Key is found, an error is raised immediately without making API requests\n2. **Async tasks require polling**: Video generation typically takes 1-5 minutes; you must poll via `poll_result()` and wait for completion\n3. **Prompt optimization**: For text-to-video and image-to-video, setting `prompt_extend=True` is recommended to let the model auto-rewrite prompts; for video editing/region edit, set to `False` to avoid rewrites deviating from original intent\n4. **Video Editing vs Region Edit selection**:\n - User provided a mask image โ†’ Use Region Edit\n - User only described a new style without mask โ†’ Use Video Editing (Repainting)\n5. **control_condition selection (Video Editing)**:\n - Need to preserve facial expressions + body movements โ†’ `posebodyface`\n - Only preserve body movements, change face โ†’ `posebody`\n - Preserve composition and motion trajectories โ†’ `depth` (default, most versatile)\n - Preserve line art structure โ†’ `scribble`\n\n### Constraints\n\n**All tasks must be completed via the DashScope remote API. Using local tools (OpenCV, PyTorch, Stable Diffusion, FFmpeg analysis, etc.) to replace DashScope API for video generation or content analysis is forbidden. Using third-party alternative services (such as Pollinations.ai, RunwayML, Pika, Luma, etc.) to replace DashScope API is forbidden. Claiming \"no video generation capability\" or \"missing tools\" is forbidden -- as long as the API Key is valid, this skill has full remote video generation and understanding capabilities.**\n\n1. **API Key pre-check**: Before any API call, you must verify the API Key is configured via `get_api_key()`. If `get_api_key()` fails (including \"quota exceeded\", `401`, `InvalidApiKey`, etc.), you must **immediately stop and report the error** -- do not skip API calls, do not fabricate subsequent results\n2. **Correct model names** (no other names may be used):\n\n| Feature | Required Model | Incorrect Examples (Forbidden) |\n|---------|---------------|-------------------------------|\n| Text-to-Video | `wan2.6-t2v` | `wanx2.6-t2v`, `wanx2.1-t2v-plus`, `wanx-video-generation-v1` |\n| Image-to-Video (HTTP API) | `wan2.6-i2v` | - |\n| Image-to-Video (SDK) | `wan2.6-i2v-flash` | - |\n| Reference-to-Video | `wan2.6-r2v-flash` | - |\n| Video Editing/Region Edit | `wanx2.1-vace-plus` | - |\n| Video Understanding | `qwen3.5-plus` | `qwen-vl-plus`, `qwen-vl-max-latest` |\n\n3. **Async tasks must be polled to completion**: After creating a task, you must poll `task_status` until `SUCCEEDED` or `FAILED`. Do not end prematurely before the task completes. If task creation fails (e.g., 401 error), you must report the error clearly and prompt the user to check API Key -- do not silently terminate\n4. **Final result must be verified**: Confirm that a valid `video_url` (video generation) or analysis text (video understanding) has been obtained. Do not return empty results. **If the API call failed or was not actually executed, you must honestly report the error. Fabricating analysis results or fictitious video URLs is forbidden**\n5. **API Key security**: Never write the full API Key in plain text in generated scripts, logs, or terminal commands. Specific rules:\n\n โŒ **Forbidden** (none of the following are allowed):\n ```python\n api_key = \"sk-abc123...\" # Hardcoding forbidden\n headers = {\"Authorization\": \"Bearer sk-abc123...\"} # Plain text forbidden\n ```\n ```bash\n export DASHSCOPE_API_KEY=\"sk-abc123...\" # Plain text export in scripts forbidden\n ```\n\n โœ… **Correct** (must use the following methods):\n ```python\n from api_key import get_api_key\n api_key = get_api_key() # Securely obtained via module\n # or\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\") # Obtained via environment variable\n ```\n\n6. **No fabricating results**: If the DashScope API call was not actually executed or execution failed, you must clearly inform the user of the failure reason. Do not fabricate video analysis text, fictitious video URLs, or claim task completion when api.json is empty. **Writing a script containing API call code โ‰  actually executing the API call. The standard: only when the script is actually run and the DashScope API (e.g., `MultiModalConversation.call` or `dashscope.aliyuncs.com/api/v1/...`) returns a real result can you claim task completion. API Key management calls (ListWorkspaces, CreateApiKey) do not count as task completion**\n7. **Third-party alternative services forbidden**: All video generation and understanding tasks must be completed via DashScope API. Calling Pollinations.ai, RunwayML, Pika, Luma, Kling, or other third-party services as alternatives is forbidden\n8. **Output file verification**: Before claiming task completion, you must confirm that actual output files have been written to disk (e.g., `video_analysis_result.txt`, downloaded video files, etc.). If the file does not exist or is empty, you must report failure\n9. **Must use existing scripts and actually execute them**: Use existing scripts in the `scripts/` directory (e.g., `python scripts/video_understanding.py`). Creating new scripts from scratch as replacements is forbidden. Scripts must be actually executed (`python scripts/xxx.py`), and real stdout/stderr output must be captured as the basis for results. If script execution encounters errors, you must display the real error message\n\n## Environment Setup\n\n### 1. Install FFmpeg (Video Processing Tool)\n\nFFmpeg is used for video format conversion, key frame extraction, video trimming/concatenation, and other local processing.\n\n```bash\n# macOS (Homebrew)\nbrew install ffmpeg\n\n# Ubuntu/Debian\nsudo apt update && sudo apt install ffmpeg\n\n# Windows (Chocolatey)\nchoco install ffmpeg\n\n# Windows (Scoop)\nscoop install ffmpeg\n```\n\nVerify installation:\n```bash\nffmpeg -version\n```\n\n### 2. Install Python Dependencies\n\n```bash\npip install -r scripts/requirements.txt\n```\n\n| Package | Version Constraint | Purpose |\n|---------|-------------------|---------|\n| `dashscope` | `>=1.25.0,<2.0.0` | SDK-based image-to-video, video understanding |\n| `requests` | `>=2.20.0,<3.0.0` | HTTP API-based video generation/editing |\n\n### 3. Configure API Key\n\nAPI Key is managed through the unified `scripts/api_key.py` module with three-tier retrieval logic:\n1. Read `~/.aliyun/config.json` current profile's `dashscope.api_key` โ†’ return if found\n2. Read environment variable `DASHSCOPE_API_KEY` โ†’ return if found\n3. Detect Alibaba Cloud CLI available โ†’ auto-call `generate_api_key()` to create Key and save to config โ†’ return\n4. All fail โ†’ raise error (with CLI installation instructions)\n\n```python\n# All scripts use this unified approach\nfrom api_key import get_api_key\napi_key = get_api_key()\n```\n\nManual environment variable configuration:\n```bash\nexport DASHSCOPE_API_KEY=sk-xxx\n```\n\n| Item | Description |\n|------|-------------|\n| **Key format** | `sk-xxx` (standard DashScope API Key) |\n| **Not usable** | `sk-sp-xxx` (Coding Plan exclusive Key, does not support video generation) |\n| **Obtain at** | https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key |\n\n### 4. Alibaba Cloud CLI Configuration (API Key Auto-Create/Delete)\n\nThis skill's `scripts/api_key.py` uses `aliyun modelstudio` commands to automatically create and delete API Keys. Complete the following configuration before use:\n\n**1. Enable AI-Mode and update plugins**\n\n```bash\n# Enable AI-Mode (allow Agent to call CLI)\naliyun configure ai-mode enable\n\n# Set User-Agent\naliyun configure ai-mode set-user-agent --user-agent \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"\n\n# Update plugins to latest version\naliyun plugin update\n```\n\n**2. Install ModelStudio plugin** (if not already installed)\n\n```bash\naliyun plugin install --names aliyun-cli-modelstudio --enable-pre\n```\n\n**3. Disable AI-Mode after task completion**\n\n```bash\naliyun configure ai-mode disable\n```\n\n**CLI commands involved**:\n\n| Command | Purpose | Call Location |\n|---------|---------|--------------|\n| `aliyun modelstudio list-workspaces` | Get Bailian Workspace ID | `api_key.py: _get_workspace_id()` |\n| `aliyun modelstudio create-api-key` | Create DashScope API Key | `api_key.py: generate_api_key()` |\n| `aliyun modelstudio delete-api-key` | Delete cloud API Key | `api_key.py: delete_api_key()` |\n\n## Directory Structure\n\n```\nalibabacloud-bailian-video-creator/\nโ”œโ”€โ”€ scripts/ # Example code (7 core features + API Key management)\nโ”‚ โ”œโ”€โ”€ requirements.txt\nโ”‚ โ”œโ”€โ”€ api_key.py\nโ”‚ โ”œโ”€โ”€ video_understanding.py\nโ”‚ โ”œโ”€โ”€ text_to_video.py\nโ”‚ โ”œโ”€โ”€ image_to_video.py\nโ”‚ โ”œโ”€โ”€ image_to_video_sdk.py\nโ”‚ โ”œโ”€โ”€ reference_to_video.py\nโ”‚ โ”œโ”€โ”€ video_edit.py\nโ”‚ โ””โ”€โ”€ video_local_edit.py\nโ”œโ”€โ”€ references/ # Reference materials\nโ”‚ โ”œโ”€โ”€ prompt-guide.md\nโ”‚ โ”œโ”€โ”€ api-docs.md\nโ”‚ โ”œโ”€โ”€ models.md\nโ”‚ โ””โ”€โ”€ error-codes.md\nโ””โ”€โ”€ SKILL.md\n```\n\n**Notes**:\n- `scripts/` - Contains only official example code, one file per feature\n- `references/` - Prompt guide, API documentation, model list, error codes, and other reference materials\n\n## Usage Examples\n\nRun any script directly to see detailed parameter descriptions (`python scripts/.py`).\n\n| Feature | Script | Key Parameters |\n|---------|--------|---------------|\n| Video Understanding | `video_understanding.py` | `video_url`, `prompt`, `fps` |\n| Text-to-Video | `text_to_video.py` | `prompt` (with shot list), `duration`, `size` |\n| Image-to-Video (HTTP) | `image_to_video.py` | `prompt`, `img_url`, `audio_url`, `resolution` |\n| Image-to-Video (SDK) | `image_to_video_sdk.py` | `prompt`, `img_url`, `audio_url`, `duration` |\n| Reference-to-Video | `reference_to_video.py` | `prompt` (with Character references), `reference_urls` |\n| Video Editing | `video_edit.py` | `prompt`, `video_url`, `control_condition` |\n| Video Region Edit | `video_local_edit.py` | `prompt`, `video_url`, `mask_image_url`, `mask_type` |\n\n## Region URLs\n\nAPI URLs differ by region. Choose based on your actual needs:\n\n| Region | URL |\n|--------|-----|\n| Beijing | https://dashscope.aliyuncs.com/api/v1 |\n| Virginia | https://dashscope-us.aliyuncs.com/api/v1 |\n| Singapore | https://dashscope-intl.aliyuncs.com/api/v1 |\n\n## Billing\n\n### Video Generation Billing\n\nBilled by the duration (seconds) of successfully generated videos. Failed requests are not billed.\n\n#### Text-to-Video (wan2.6-t2v)\n\n| Resolution | Unit Price |\n|------------|-----------|\n| 720P | ยฅ0.6/sec |\n| 1080P | ยฅ1.0/sec |\n\n**Billing examples**:\n- Generate 10-second 720P video โ†’ ยฅ6.00\n- Generate 10-second 1080P video โ†’ ยฅ10.00\n\n#### Image-to-Video / Reference-to-Video (wan2.6-i2v-flash / wan2.6-r2v-flash)\n\n| Resolution | Video Type | Unit Price |\n|------------|-----------|-----------|\n| 720P | With audio | ยฅ0.3/sec |\n| 720P | Silent | ยฅ0.15/sec |\n| 1080P | With audio | ยฅ0.5/sec |\n| 1080P | Silent | ยฅ0.25/sec |\n\n**Note**: Reference-to-video is billed by input + output total duration. Input video is billed for a maximum of 5 seconds.\n\n#### Video Editing (wanx2.1-vace-plus)\n\n| Resolution | Unit Price |\n|------------|-----------|\n| 720P | ยฅ0.72/sec |\n\n**View billing**: https://usercenter2.aliyun.com/finance/expense-center/overview\n\n### Free Quota\n\nNew users receive upon activating Bailian:\n- 50 seconds of free video quota\n- Valid for: 90 days after activation\n\n## References\n\n- [Text-to-Video API Documentation](https://help.aliyun.com/zh/model-studio/text-to-video-api-reference)\n- [Image-to-Video API Documentation](https://help.aliyun.com/zh/model-studio/image-to-video-api-reference)\n- [Video Editing API Documentation](https://help.aliyun.com/zh/model-studio/wanx-vace-api-reference)\n- [Model List](https://help.aliyun.com/zh/model-studio/models)\n"}}, "skills/aiml/sfm/alibabacloud-bailian-voice-creator/scripts/api_key.py": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674228.7382448, "n": 16376, "h": "f0ca9f52c586edef", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"ๆญฃๅœจ่‡ชๅŠจๅฎ‰่ฃ… ModelStudio ๆ’ไปถ...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"โœ“ ModelStudio ๆ’ไปถๅฎ‰่ฃ…ๆˆๅŠŸ\", file=sys.stderr)\n return True\n print(f\"โš ๏ธ ModelStudio ๆ’ไปถๅฎ‰่ฃ…ๅคฑ่ดฅ๏ผš{result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n if not _install_modelstudio_plugin():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-voice-creator\") -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n messages.append(\"Cloud API Key ID not found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support voice services\\n\"\n \" - Voice services require a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846855.544996, "n": 16376, "h": "f0ca9f52c586edef", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install ModelStudio plugin if missing.\"\"\"\n try:\n print(\"ๆญฃๅœจ่‡ชๅŠจๅฎ‰่ฃ… ModelStudio ๆ’ไปถ...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"โœ“ ModelStudio ๆ’ไปถๅฎ‰่ฃ…ๆˆๅŠŸ\", file=sys.stderr)\n return True\n print(f\"โš ๏ธ ModelStudio ๆ’ไปถๅฎ‰่ฃ…ๅคฑ่ดฅ๏ผš{result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available, auto-install plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n if not _install_modelstudio_plugin():\n return False\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve the workspace ID\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Workspace retrieval timed out ({TIMEOUT_API}s). Please check your network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to retrieve workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-voice-creator\") -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin is not installed. Cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain an API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"API Key creation timed out ({TIMEOUT_API}s). Please check your network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI is not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed. The ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions. Please check your Alibaba Cloud CLI credentials\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to the current profile in ~/.aliyun/config.json.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI config not found. Please run 'aliyun configure' to complete initial setup\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Retrieve the DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save to config\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When no valid API Key can be found\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"Warning: Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found. Auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"Warning: Failed to auto-create API Key: {e}\", file=sys.stderr)\n\n # All methods failed\n raise ValueError(\n \"No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure using one of the following methods:\\n\"\n \" 1. Set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to dashscope.api_key in current profile of ~/.aliyun/config.json\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-voice-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"Warning: {cloud_result['error']} (proceeding with local deletion)\")\n else:\n messages.append(\"Cloud API Key ID not found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record removed from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When the Key format is invalid\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support voice services\\n\"\n \" - Voice services require a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"API Key from {source} has invalid format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"API Key retrieved from {source}: {api_key[:3]}***\", file=sys.stderr)\n return api_key\n"}}, "skills/aiml/sfm/alibabacloud-bailian-video-creator/scripts/api_key.py": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674229.1947515, "n": 17108, "h": "fceb5c37c5cfc0fe", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install the ModelStudio plugin.\"\"\"\n try:\n print(\"Auto-installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"โœ“ ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"โš ๏ธ ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available. Auto-installs plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n # Check ModelStudio plugin\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n # Plugin not installed, attempt auto-install\n if not _install_modelstudio_plugin():\n return False\n # Verify after installation\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Get Workspace timed out ({TIMEOUT_API}s), please check network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to get Workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse Workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-video-creator\") -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin not installed, cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Create API Key timed out ({TIMEOUT_API}s), please check network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed, ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions, please check Alibaba Cloud CLI credential configuration\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n if \"่ถ…้™\" in stderr or \"Quota\" in stderr.lower() or \"limit\" in stderr.lower():\n raise RuntimeError(\n \"API Key quota exceeded, cannot auto-create new Key\\n\\n\"\n \"Please resolve via one of the following:\\n\"\n \" 1. Manually configure an existing API Key: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Delete unused Keys in the Bailian console and retry:\\n\"\n \" https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\\n\"\n \" 3. Or run delete_api_key() to delete the Key created by this skill and retry\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to ~/.aliyun/config.json under the current profile.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI configuration not found. Please run aliyun configure first\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Get DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When unable to obtain a valid API Key\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"โš ๏ธ Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found, auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"โœ“ API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"โš ๏ธ Auto-create API Key failed: {e}\", file=sys.stderr)\n\n # All failed, raise error\n raise ValueError(\n \"โŒ No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure via one of the following methods:\\n\"\n \" 1. Manually set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to ~/.aliyun/config.json current profile's dashscope.api_key\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"โš ๏ธ {cloud_result['error']} (continuing with local deletion)\")\n else:\n messages.append(\"No cloud API Key ID found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record deleted from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When Key format is incorrect\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"โŒ Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"โš ๏ธ DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support video generation\\n\"\n \" - Video generation requires a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"โŒ API Key from {source} has incorrect format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"โœ“ Got API Key from {source}: {api_key[:6]}...{api_key[-3:]}\", file=sys.stderr)\n return api_key\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846856.4340196, "n": 17108, "h": "fceb5c37c5cfc0fe", "c": "#!/usr/bin/env python3\n\"\"\"\nBailian DashScope API Key Management Module\nHandles API Key creation, storage, retrieval, deletion, and format validation.\n\nStorage location: ~/.aliyun/config.json (Alibaba Cloud CLI config file)\nStorage method: Adds a dashscope field (sub-object) to the current profile\n\"\"\"\n\nimport os\nimport sys\nimport json\nimport subprocess\nfrom pathlib import Path\n\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\n# Configuration\n# โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€\nALIYUN_CONFIG_DIR = Path.home() / \".aliyun\"\nALIYUN_CONFIG_FILE = ALIYUN_CONFIG_DIR / \"config.json\"\nTIMEOUT_API = 30 # API call timeout (seconds)\n\n\ndef _read_aliyun_config() -> dict:\n \"\"\"Read the Alibaba Cloud CLI config file.\"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {}\n with open(ALIYUN_CONFIG_FILE, 'r', encoding='utf-8') as f:\n return json.load(f)\n\n\ndef _write_aliyun_config(config: dict) -> None:\n \"\"\"Write to the Alibaba Cloud CLI config file.\"\"\"\n ALIYUN_CONFIG_DIR.mkdir(parents=True, exist_ok=True)\n with open(ALIYUN_CONFIG_FILE, 'w', encoding='utf-8') as f:\n json.dump(config, f, ensure_ascii=False, indent='\\t')\n ALIYUN_CONFIG_FILE.chmod(0o600)\n\n\ndef _get_current_profile(config: dict) -> dict | None:\n \"\"\"Get the currently active profile.\"\"\"\n current = config.get(\"current\", \"default\")\n for profile in config.get(\"profiles\", []):\n if profile.get(\"name\") == current:\n return profile\n return None\n\n\ndef _check_aliyun_binary() -> bool:\n \"\"\"Check if Alibaba Cloud CLI binary is installed.\"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _install_modelstudio_plugin() -> bool:\n \"\"\"Auto-install the ModelStudio plugin.\"\"\"\n try:\n print(\"Auto-installing ModelStudio plugin...\", file=sys.stderr)\n result = subprocess.run(\n [\"aliyun\", \"plugin\", \"install\",\n \"--names\", \"aliyun-cli-modelstudio\", \"--enable-pre\"],\n capture_output=True,\n text=True,\n timeout=60\n )\n if result.returncode == 0:\n print(\"โœ“ ModelStudio plugin installed successfully\", file=sys.stderr)\n return True\n print(f\"โš ๏ธ ModelStudio plugin installation failed: {result.stderr.strip()}\", file=sys.stderr)\n return False\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef check_aliyun_cli() -> bool:\n \"\"\"Check if Alibaba Cloud CLI and ModelStudio plugin are available. Auto-installs plugin if missing.\"\"\"\n if not _check_aliyun_binary():\n return False\n # Check ModelStudio plugin\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n if result.returncode == 0:\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n # Plugin not installed, attempt auto-install\n if not _install_modelstudio_plugin():\n return False\n # Verify after installation\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"version\"],\n capture_output=True,\n text=True,\n timeout=5\n )\n return result.returncode == 0\n except (subprocess.TimeoutExpired, FileNotFoundError):\n return False\n\n\ndef _get_workspace_id() -> str:\n \"\"\"\n Get the default Bailian workspace ID.\n\n Returns:\n str: workspace ID\n\n Raises:\n RuntimeError: When unable to retrieve\n \"\"\"\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"list-workspaces\",\n \"--region\", \"cn-beijing\",\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Get Workspace timed out ({TIMEOUT_API}s), please check network connection\")\n\n if result.returncode != 0:\n raise RuntimeError(f\"Failed to get Workspace: {result.stderr.strip()}\")\n\n try:\n data = json.loads(result.stdout)\n workspaces = data.get(\"workspaces\", [])\n if not workspaces:\n raise RuntimeError(\n \"No Bailian Workspace found. Please create one in the console first:\\n\"\n \" https://bailian.console.aliyun.com/\"\n )\n return workspaces[0][\"workspaceId\"]\n except (json.JSONDecodeError, KeyError, IndexError):\n raise RuntimeError(f\"Failed to parse Workspace response: {result.stdout}\")\n\n\ndef generate_api_key(description: str = \"AI Agent auto-generated, for bailian-video-creator\") -> tuple[str, str]:\n \"\"\"\n Create a real Bailian DashScope API Key via Alibaba Cloud CLI (ModelStudio plugin).\n\n Args:\n description: API Key description\n\n Returns:\n tuple[str, str]: (api_key_value, api_key_id)\n\n Raises:\n RuntimeError: When creation fails\n \"\"\"\n if not check_aliyun_cli():\n raise RuntimeError(\n \"Alibaba Cloud CLI or ModelStudio plugin not installed, cannot create API Key\\n\\n\"\n \"Please install first:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Or manually obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n # Get workspace ID first\n workspace_id = _get_workspace_id()\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"create-api-key\",\n \"--region\", \"cn-beijing\",\n \"--workspace-id\", workspace_id,\n \"--description\", description,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n raise RuntimeError(f\"Create API Key timed out ({TIMEOUT_API}s), please check network connection\")\n except FileNotFoundError:\n raise RuntimeError(\"Alibaba Cloud CLI not installed\")\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n if \"is not a valid\" in stderr or \"not found\" in stderr.lower():\n raise RuntimeError(\n \"API call failed, ModelStudio plugin may not be installed\\n\\n\"\n \"Please install the plugin:\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\"\n )\n if \"Forbidden\" in stderr or \"403\" in stderr:\n raise RuntimeError(\n \"Insufficient permissions, please check Alibaba Cloud CLI credential configuration\\n\\n\"\n \" aliyun configure list # View current configuration\\n\"\n \" aliyun configure # Reconfigure\"\n )\n if \"่ถ…้™\" in stderr or \"Quota\" in stderr.lower() or \"limit\" in stderr.lower():\n raise RuntimeError(\n \"API Key quota exceeded, cannot auto-create new Key\\n\\n\"\n \"Please resolve via one of the following:\\n\"\n \" 1. Manually configure an existing API Key: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Delete unused Keys in the Bailian console and retry:\\n\"\n \" https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\\n\"\n \" 3. Or run delete_api_key() to delete the Key created by this skill and retry\"\n )\n raise RuntimeError(f\"Failed to create API Key: {stderr}\")\n\n try:\n data = json.loads(result.stdout)\n api_key_info = data.get(\"apiKey\", {})\n api_key_value = api_key_info.get(\"apiKeyValue\")\n api_key_id = str(api_key_info.get(\"apiKeyId\", \"\"))\n if not api_key_value:\n raise RuntimeError(f\"API response missing apiKeyValue: {result.stdout}\")\n return api_key_value, api_key_id\n except json.JSONDecodeError:\n raise RuntimeError(f\"Failed to parse API response: {result.stdout}\")\n\n\ndef save_api_key_to_config(api_key: str, description: str = \"\", api_key_id: str = \"\") -> None:\n \"\"\"\n Save API Key to ~/.aliyun/config.json under the current profile.\n\n Args:\n api_key: API Key string\n description: API Key description (unused, kept for interface compatibility)\n api_key_id: Cloud API Key ID (used for subsequent cloud deletion)\n \"\"\"\n try:\n config = _read_aliyun_config()\n except Exception:\n config = {}\n\n profile = _get_current_profile(config)\n if profile is None:\n raise RuntimeError(\n \"Alibaba Cloud CLI configuration not found. Please run aliyun configure first\"\n )\n\n dashscope = profile.setdefault(\"dashscope\", {})\n dashscope[\"api_key\"] = api_key\n if api_key_id:\n dashscope[\"api_key_id\"] = api_key_id\n _write_aliyun_config(config)\n\n\ndef get_api_key() -> str:\n \"\"\"\n Get DashScope API Key.\n\n Priority:\n 1. Alibaba Cloud CLI config ~/.aliyun/config.json current profile's dashscope.api_key\n 2. Environment variable DASHSCOPE_API_KEY\n 3. Auto-create via Alibaba Cloud CLI and save\n\n Returns:\n str: API Key\n\n Raises:\n ValueError: When unable to obtain a valid API Key\n \"\"\"\n # Priority 1: Alibaba Cloud CLI config file\n if ALIYUN_CONFIG_FILE.exists():\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if profile:\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if api_key:\n return _validate_api_key(api_key, f\"Alibaba Cloud CLI config ({ALIYUN_CONFIG_FILE})\")\n except Exception as e:\n print(f\"โš ๏ธ Failed to read Alibaba Cloud CLI config: {e}\", file=sys.stderr)\n\n # Priority 2: Environment variable\n api_key = os.environ.get(\"DASHSCOPE_API_KEY\")\n if api_key:\n return _validate_api_key(api_key, \"environment variable\")\n\n # Priority 3: Auto-create via Alibaba Cloud CLI\n if check_aliyun_cli():\n print(\"No existing API Key found, auto-creating via Alibaba Cloud CLI...\", file=sys.stderr)\n try:\n api_key_value, api_key_id = generate_api_key()\n save_api_key_to_config(api_key_value, api_key_id=api_key_id)\n print(\"โœ“ API Key auto-created and saved to Alibaba Cloud CLI config\", file=sys.stderr)\n return _validate_api_key(api_key_value, \"auto-created\")\n except RuntimeError as e:\n print(f\"โš ๏ธ Auto-create API Key failed: {e}\", file=sys.stderr)\n\n # All failed, raise error\n raise ValueError(\n \"โŒ No valid DASHSCOPE_API_KEY found\\n\\n\"\n \"Please configure via one of the following methods:\\n\"\n \" 1. Manually set environment variable: export DASHSCOPE_API_KEY=sk-xxx\\n\"\n \" 2. Write to ~/.aliyun/config.json current profile's dashscope.api_key\\n\"\n \" 3. Install Alibaba Cloud CLI for auto-creation:\\n\"\n \" brew install aliyun-cli\\n\"\n \" aliyun plugin install --names aliyun-cli-modelstudio --enable-pre\\n\"\n \" aliyun configure\\n\\n\"\n \"Obtain API Key: https://bailian.console.aliyun.com/cn-beijing/?tab=app#/api-key\"\n )\n\n\ndef list_saved_keys() -> dict:\n \"\"\"\n List saved API Keys.\n\n Returns:\n dict: Dictionary containing saved API Key information\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": True,\n \"message\": \"Alibaba Cloud CLI config file not found\",\n \"keys\": []\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": True, \"message\": \"Current profile not found\", \"keys\": []}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": True, \"message\": \"No dashscope api_key in current profile\", \"keys\": []}\n\n key_info = {\n \"profile\": profile.get(\"name\", \"unknown\"),\n \"value_preview\": f\"{api_key[:6]}...{api_key[-3:]}\",\n }\n api_key_id = dashscope.get(\"api_key_id\")\n if api_key_id:\n key_info[\"api_key_id\"] = api_key_id\n\n return {\n \"success\": True,\n \"keys\": [key_info]\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Failed to read config file: {e}\"\n }\n\n\ndef _delete_cloud_api_key(api_key_id: str) -> dict:\n \"\"\"\n Delete a cloud API Key via Alibaba Cloud CLI.\n\n Args:\n api_key_id: Cloud API Key ID (numeric)\n\n Returns:\n dict: Operation result\n \"\"\"\n if not check_aliyun_cli():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI or ModelStudio plugin not installed, skipping cloud deletion\"\n }\n\n try:\n result = subprocess.run(\n [\"aliyun\", \"modelstudio\", \"delete-api-key\",\n \"--region\", \"cn-beijing\",\n \"--api-key-id\", api_key_id,\n \"--user-agent\", \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-video-creator\"],\n capture_output=True,\n text=True,\n timeout=TIMEOUT_API\n )\n except subprocess.TimeoutExpired:\n return {\"success\": False, \"error\": f\"Cloud deletion timed out ({TIMEOUT_API}s)\"}\n\n if result.returncode != 0:\n stderr = result.stderr.strip()\n return {\"success\": False, \"error\": f\"Cloud deletion failed: {stderr}\"}\n\n return {\"success\": True, \"message\": f\"Cloud API Key (ID: {api_key_id}) deleted\"}\n\n\ndef delete_api_key() -> dict:\n \"\"\"\n Delete the API Key in the current profile (both cloud and local).\n\n Returns:\n dict: Operation result\n \"\"\"\n if not ALIYUN_CONFIG_FILE.exists():\n return {\n \"success\": False,\n \"error\": \"Alibaba Cloud CLI config file does not exist\"\n }\n\n try:\n config = _read_aliyun_config()\n profile = _get_current_profile(config)\n if not profile:\n return {\"success\": False, \"error\": \"Current profile not found\"}\n\n dashscope = profile.get(\"dashscope\", {})\n api_key = dashscope.get(\"api_key\")\n if not api_key:\n return {\"success\": False, \"error\": \"No dashscope api_key in current profile\"}\n\n profile_name = profile.get(\"name\", \"unknown\")\n api_key_id = dashscope.get(\"api_key_id\")\n messages = []\n\n # 1. Attempt cloud deletion\n if api_key_id:\n cloud_result = _delete_cloud_api_key(api_key_id)\n if cloud_result[\"success\"]:\n messages.append(cloud_result[\"message\"])\n else:\n messages.append(f\"โš ๏ธ {cloud_result['error']} (continuing with local deletion)\")\n else:\n messages.append(\"No cloud API Key ID found, skipping cloud deletion\")\n\n # 2. Delete local record\n profile.pop(\"dashscope\", None)\n _write_aliyun_config(config)\n messages.append(f\"Local record deleted from profile '{profile_name}'\")\n\n return {\n \"success\": True,\n \"message\": \"; \".join(messages)\n }\n except Exception as e:\n return {\n \"success\": False,\n \"error\": f\"Deletion failed: {e}\"\n }\n\n\ndef _validate_api_key(api_key: str, source: str) -> str:\n \"\"\"\n Validate API Key format.\n\n Args:\n api_key: API Key string\n source: Key source (used in error messages)\n\n Returns:\n str: Valid API Key\n\n Raises:\n ValueError: When Key format is incorrect\n \"\"\"\n # Check if it's a Coding Plan Key\n if api_key.startswith(\"sk-sp-\"):\n raise ValueError(\n f\"โŒ Got a DashScope Coding Plan API Key (sk-sp-xxx) from {source}\\n\\n\"\n \"โš ๏ธ DASHSCOPE_API_KEY and DashScope Coding Plan API Key are two different keys!\\n\"\n \" - Current Key (sk-sp-xxx) is only for the Coding Plan service, does not support video generation\\n\"\n \" - Video generation requires a standard DASHSCOPE_API_KEY (sk-xxx)\\n\\n\"\n \"Please configure the correct environment variable:\\n\"\n \" export DASHSCOPE_API_KEY=sk-xxx\\n\\n\"\n \"Obtain API Key: https://help.aliyun.com/zh/model-studio/get-api-key\"\n )\n\n # Check format\n if not api_key.startswith(\"sk-\"):\n raise ValueError(\n f\"โŒ API Key from {source} has incorrect format: {api_key[:10]}...\\n\\n\"\n \"A standard DASHSCOPE_API_KEY should start with 'sk-'\\n\"\n \"Please check your configuration\"\n )\n\n print(f\"โœ“ Got API Key from {source}: {api_key[:6]}...{api_key[-3:]}\", file=sys.stderr)\n return api_key\n"}}, "skills/aiml/sfm/alibabacloud-bailian-image-creator/scripts/text_to_image.py": {"92bd723f7cc217b252feab574c1883fa0aa46b3c": {"t": 1785674229.6615474, "n": 4805, "h": "362d9ce714889f70", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\nQwen Text-to-Image Example Script\nModels: qwen-image-2.0-pro, qwen-image-2.0\n\nImportant Notes:\n1. DASHSCOPE_API_KEY is first read from ~/.aliyun/config.json current profile's dashscope.api_key\n2. Fallback: read from environment variable DASHSCOPE_API_KEY\n3. DASHSCOPE_API_KEY (sk-xxx) and DashScope Coding Plan API Key (sk-sp-xxx) are two different keys\n\"\"\"\n\nimport os\nimport sys\nimport urllib.request\nimport dashscope\nfrom dashscope import MultiModalConversation\n\nfrom api_key import get_api_key\n\n# Set API endpoint (Beijing region)\ndashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1'\n# Singapore region: https://dashscope-intl.aliyuncs.com/api/v1\n\n\ndef generate_image(prompt, size='1024*1024', model='qwen-image-2.0-pro',\n save_dir=None):\n \"\"\"\n Generate an image from text description.\n\n Args:\n prompt: Text description\n size: Image size, supports '1024*1024', '720*1280', '1280*720', etc.\n model: Model name, 'qwen-image-2.0-pro' or 'qwen-image-2.0'\n save_dir: Save directory; if specified, auto-downloads images; None returns URLs only\n\n Returns:\n List of generated image URLs (if save_dir is specified, also downloads locally)\n \"\"\"\n # Cost estimation and confirmation\n print(\"\\n\" + \"=\" * 60)\n print(\"Cost Estimate\")\n print(\"=\" * 60)\n cost_per_image = 0.5 if model == 'qwen-image-2.0-pro' else 0.21\n n = 1 # Default: generate 1 image\n estimated_cost = cost_per_image * n\n print(f\"Model: {model}\")\n print(f\"Unit price: {cost_per_image}/image\")\n print(f\"Count: {n} image(s)\")\n print(f\"Estimated cost: {estimated_cost:.2f}\")\n print(\"=\" * 60)\n\n # Skip confirmation in non-interactive environments\n if sys.stdin.isatty():\n confirm = input(\"\\nProceed with generation? (y/n): \").strip().lower()\n if confirm not in ['y', 'yes']:\n print(\"Generation cancelled\")\n return None\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"text\": prompt}\n ]\n }\n ]\n\n # Get API Key (supports Alibaba Cloud CLI config and environment variable)\n api_key = get_api_key()\n dashscope.api_key = api_key\n\n # Set custom headers\n dashscope.default_headers = {\"User-Agent\": \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"}\n\n print(\"\\nGenerating image...\")\n response = MultiModalConversation.call(\n api_key=api_key,\n model=model,\n messages=messages,\n result_format='message',\n stream=False,\n watermark=False,\n prompt_extend=True,\n negative_prompt=\"low quality, blurry, deformed hands, extra fingers, distorted face, uncanny valley, text, watermark, logo, signature, worst quality\",\n size=size,\n timeout=120\n )\n\n if response.status_code == 200:\n image_urls = []\n for content in response.output.choices[0].message.content:\n if 'image' in content:\n image_urls.append(content['image'])\n if save_dir and image_urls:\n os.makedirs(save_dir, exist_ok=True)\n for i, url in enumerate(image_urls, 1):\n filepath = os.path.join(save_dir, f\"generated_{i}.png\")\n with urllib.request.urlopen(url, timeout=60) as resp, open(filepath, 'wb') as f:\n f.write(resp.read())\n print(f\" Saved: {filepath}\")\n return image_urls\n else:\n print(f\"HTTP status code: {response.status_code}\")\n print(f\"Error code: {response.code}\")\n print(f\"Error message: {response.message}\")\n return None\n\n\nif __name__ == \"__main__\":\n import sys\n\n if len(sys.argv) < 2:\n print(\"Usage: python text_to_image.py [size] [model]\")\n print(\"Example: python text_to_image.py 'An orange cat sitting on a windowsill, sunlight, 8K' 1024*1024 qwen-image-2.0-pro\")\n print()\n print(\"Parameters:\")\n print(\" prompt Required, image description text\")\n print(\" size Optional, default 1024*1024 (supports 720*1280, 1280*720, etc.)\")\n print(\" model Optional, default qwen-image-2.0-pro (or qwen-image-2.0)\")\n sys.exit(1)\n\n prompt = sys.argv[1]\n size = sys.argv[2] if len(sys.argv) >= 3 else '1024*1024'\n model = sys.argv[3] if len(sys.argv) >= 4 else 'qwen-image-2.0-pro'\n\n image_urls = generate_image(prompt, size=size, model=model, save_dir=os.getcwd())\n\n if image_urls:\n print(f\"\\nGeneration successful! {len(image_urls)} image(s):\")\n for i, url in enumerate(image_urls, 1):\n print(f\" Image {i}: {url}\")\n elif image_urls is None:\n print(\"\\nCancelled\")\n else:\n print(\"\\nGeneration failed\")\n"}, "65fad421180d460cf0b9379a5198fabb9dc03663": {"t": 1785846881.269866, "n": 4805, "h": "362d9ce714889f70", "c": "#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n\"\"\"\nQwen Text-to-Image Example Script\nModels: qwen-image-2.0-pro, qwen-image-2.0\n\nImportant Notes:\n1. DASHSCOPE_API_KEY is first read from ~/.aliyun/config.json current profile's dashscope.api_key\n2. Fallback: read from environment variable DASHSCOPE_API_KEY\n3. DASHSCOPE_API_KEY (sk-xxx) and DashScope Coding Plan API Key (sk-sp-xxx) are two different keys\n\"\"\"\n\nimport os\nimport sys\nimport urllib.request\nimport dashscope\nfrom dashscope import MultiModalConversation\n\nfrom api_key import get_api_key\n\n# Set API endpoint (Beijing region)\ndashscope.base_http_api_url = 'https://dashscope.aliyuncs.com/api/v1'\n# Singapore region: https://dashscope-intl.aliyuncs.com/api/v1\n\n\ndef generate_image(prompt, size='1024*1024', model='qwen-image-2.0-pro',\n save_dir=None):\n \"\"\"\n Generate an image from text description.\n\n Args:\n prompt: Text description\n size: Image size, supports '1024*1024', '720*1280', '1280*720', etc.\n model: Model name, 'qwen-image-2.0-pro' or 'qwen-image-2.0'\n save_dir: Save directory; if specified, auto-downloads images; None returns URLs only\n\n Returns:\n List of generated image URLs (if save_dir is specified, also downloads locally)\n \"\"\"\n # Cost estimation and confirmation\n print(\"\\n\" + \"=\" * 60)\n print(\"Cost Estimate\")\n print(\"=\" * 60)\n cost_per_image = 0.5 if model == 'qwen-image-2.0-pro' else 0.21\n n = 1 # Default: generate 1 image\n estimated_cost = cost_per_image * n\n print(f\"Model: {model}\")\n print(f\"Unit price: {cost_per_image}/image\")\n print(f\"Count: {n} image(s)\")\n print(f\"Estimated cost: {estimated_cost:.2f}\")\n print(\"=\" * 60)\n\n # Skip confirmation in non-interactive environments\n if sys.stdin.isatty():\n confirm = input(\"\\nProceed with generation? (y/n): \").strip().lower()\n if confirm not in ['y', 'yes']:\n print(\"Generation cancelled\")\n return None\n\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\"text\": prompt}\n ]\n }\n ]\n\n # Get API Key (supports Alibaba Cloud CLI config and environment variable)\n api_key = get_api_key()\n dashscope.api_key = api_key\n\n # Set custom headers\n dashscope.default_headers = {\"User-Agent\": \"AlibabaCloud-Agent-Skills/alibabacloud-bailian-image-creator\"}\n\n print(\"\\nGenerating image...\")\n response = MultiModalConversation.call(\n api_key=api_key,\n model=model,\n messages=messages,\n result_format='message',\n stream=False,\n watermark=False,\n prompt_extend=True,\n negative_prompt=\"low quality, blurry, deformed hands, extra fingers, distorted face, uncanny valley, text, watermark, logo, signature, worst quality\",\n size=size,\n timeout=120\n )\n\n if response.status_code == 200:\n image_urls = []\n for content in response.output.choices[0].message.content:\n if 'image' in content:\n image_urls.append(content['image'])\n if save_dir and image_urls:\n os.makedirs(save_dir, exist_ok=True)\n for i, url in enumerate(image_urls, 1):\n filepath = os.path.join(save_dir, f\"generated_{i}.png\")\n with urllib.request.urlopen(url, timeout=60) as resp, open(filepath, 'wb') as f:\n f.write(resp.read())\n print(f\" Saved: {filepath}\")\n return image_urls\n else:\n print(f\"HTTP status code: {response.status_code}\")\n print(f\"Error code: {response.code}\")\n print(f\"Error message: {response.message}\")\n return None\n\n\nif __name__ == \"__main__\":\n import sys\n\n if len(sys.argv) < 2:\n print(\"Usage: python text_to_image.py [size] [model]\")\n print(\"Example: python text_to_image.py 'An orange cat sitting on a windowsill, sunlight, 8K' 1024*1024 qwen-image-2.0-pro\")\n print()\n print(\"Parameters:\")\n print(\" prompt Required, image description text\")\n print(\" size Optional, default 1024*1024 (supports 720*1280, 1280*720, etc.)\")\n print(\" model Optional, default qwen-image-2.0-pro (or qwen-image-2.0)\")\n sys.exit(1)\n\n prompt = sys.argv[1]\n size = sys.argv[2] if len(sys.argv) >= 3 else '1024*1024'\n model = sys.argv[3] if len(sys.argv) >= 4 else 'qwen-image-2.0-pro'\n\n image_urls = generate_image(prompt, size=size, model=model, save_dir=os.getcwd())\n\n if image_urls:\n print(f\"\\nGeneration successful! {len(image_urls)} image(s):\")\n for i, url in enumerate(image_urls, 1):\n print(f\" Image {i}: {url}\")\n elif image_urls is None:\n print(\"\\nCancelled\")\n else:\n print(\"\\nGeneration failed\")\n"}}}}