@aiyiran/myclaw 1.0.201 → 1.0.203

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/index.js +113 -25
  2. package/{inject-clear-models.js → injects/inject-clear-models.js} +1 -1
  3. package/{inject-image.js → injects/inject-image.js} +1 -1
  4. package/{inject-minimax.js → injects/inject-minimax.js} +1 -1
  5. package/{inject-search.js → injects/inject-search.js} +1 -1
  6. package/{inject-token.js → injects/inject-token.js} +1 -1
  7. package/injects/inject-tooldeny.js +50 -0
  8. package/{inject-zai.js → injects/inject-zai.js} +1 -1
  9. package/package.json +1 -1
  10. package/{patch-manifest.json → patches/patch-manifest.json} +15 -5
  11. package/{patch-reset.js → patches/patch-reset.js} +1 -1
  12. package/{patch-skill.js → patches/patch-skill.js} +6 -1
  13. package/pull.js +1 -1
  14. package/skills/vapi-image-gen-1.0.1/README.md +92 -0
  15. package/skills/vapi-image-gen-1.0.1/SKILL.md +75 -0
  16. package/skills/vapi-image-gen-1.0.1/_meta.json +6 -0
  17. package/skills/vapi-image-gen-1.0.1/scripts/gen.py +259 -0
  18. package/skills/yiran-skill-media/SKILL.md +74 -0
  19. package/skills/yiran-skill-media/config.json +26 -0
  20. package/skills/yiran-skill-media/references/image-api.md +88 -0
  21. package/skills/yiran-skill-media/references/music-api.md +120 -0
  22. package/skills/yiran-skill-media/scripts/generate.py +165 -0
  23. package/skills/yiran-skill-media/scripts/generation_log.json +20 -0
  24. package/skills/yiran-skill-media/scripts/image.sh +43 -0
  25. package/skills/yiran-skill-media/scripts/music.sh +46 -0
  26. package/skills/yiran-skill-media/scripts/providers/__init__.py +15 -0
  27. package/skills/yiran-skill-media/scripts/providers/__pycache__/__init__.cpython-311.pyc +0 -0
  28. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_image.cpython-311.pyc +0 -0
  29. package/skills/yiran-skill-media/scripts/providers/__pycache__/minimax_music.cpython-311.pyc +0 -0
  30. package/skills/yiran-skill-media/scripts/providers/__pycache__/vapi_image.cpython-311.pyc +0 -0
  31. package/skills/yiran-skill-media/scripts/providers/minimax_image.py +75 -0
  32. package/skills/yiran-skill-media/scripts/providers/minimax_music.py +61 -0
  33. package/skills/yiran-skill-media/scripts/providers/vapi_image.py +63 -0
  34. package/skills/yiran-skill-media/scripts/registry.py +133 -0
  35. /package/{inject-workspaceAndSoul.js → injects/inject-workspaceAndSoul.js} +0 -0
  36. /package/{patch-agent.js → patches/patch-agent.js} +0 -0
  37. /package/{patch.js → patches/patch.js} +0 -0
@@ -0,0 +1,259 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ VAPI Image Generation & Editing Script
4
+ - Generate: POST /images/generations
5
+ - Edit: POST /images/edits (when --input is provided)
6
+ """
7
+ import argparse
8
+ import base64
9
+ import json
10
+ import os
11
+ import re
12
+ import sys
13
+ import urllib.error
14
+ import urllib.request
15
+ from datetime import datetime
16
+ from pathlib import Path
17
+
18
+
19
+ def get_default_save_dir() -> Path:
20
+ return Path.home() / ".openclaw" / "media"
21
+
22
+
23
+ def get_oss_dir() -> Path:
24
+ return Path.home() / ".openclaw" / "oss"
25
+
26
+
27
+ def slugify(text: str) -> str:
28
+ text = text.lower().strip()
29
+ text = re.sub(r"[^a-z0-9]+", "-", text)
30
+ text = re.sub(r"-{2,}", "-", text).strip("-")
31
+ return text[:50] or "image"
32
+
33
+
34
+ def download_to_temp(url: str) -> Path:
35
+ """Download remote image to a temp file with chunked read."""
36
+ import tempfile
37
+ suffix = ".jpg" if ".jpg" in url.lower() else ".png"
38
+ tmp = tempfile.NamedTemporaryFile(delete=False, suffix=suffix)
39
+ req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
40
+ with urllib.request.urlopen(req, timeout=60) as resp:
41
+ with open(tmp.name, "wb") as f:
42
+ while True:
43
+ chunk = resp.read(65536)
44
+ if not chunk:
45
+ break
46
+ f.write(chunk)
47
+ return Path(tmp.name)
48
+
49
+
50
+ def request_generate(
51
+ base_url: str,
52
+ api_key: str,
53
+ prompt: str,
54
+ model: str,
55
+ response_format: str,
56
+ size: str = "",
57
+ aspect_ratio: str = "",
58
+ count: int = 1,
59
+ ) -> dict:
60
+ """POST /images/generations"""
61
+ url = f"{base_url.rstrip('/')}/images/generations"
62
+ payload = {
63
+ "model": model,
64
+ "prompt": prompt,
65
+ "n": count,
66
+ "response_format": response_format,
67
+ }
68
+ if size:
69
+ payload["size"] = size
70
+ if aspect_ratio:
71
+ payload["aspect_ratio"] = aspect_ratio
72
+
73
+ body = json.dumps(payload).encode("utf-8")
74
+ req = urllib.request.Request(
75
+ url, method="POST",
76
+ headers={"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"},
77
+ data=body,
78
+ )
79
+ try:
80
+ with urllib.request.urlopen(req, timeout=120) as resp:
81
+ return json.loads(resp.read().decode("utf-8"))
82
+ except urllib.error.HTTPError as e:
83
+ raise RuntimeError(f"VAPI API error ({e.code}): {e.read().decode('utf-8', errors='replace')}") from e
84
+
85
+
86
+ def request_edit(
87
+ base_url: str,
88
+ api_key: str,
89
+ prompt: str,
90
+ model: str,
91
+ image_path: Path,
92
+ response_format: str,
93
+ size: str = "",
94
+ aspect_ratio: str = "",
95
+ count: int = 1,
96
+ ) -> dict:
97
+ """POST /images/edits (multipart form data)"""
98
+ url = f"{base_url.rstrip('/')}/images/edits"
99
+
100
+ # Build multipart form data manually
101
+ boundary = "----VAPIBoundary" + os.urandom(8).hex()
102
+ parts = []
103
+
104
+ def field(name: str, value: str) -> bytes:
105
+ return (
106
+ f"--{boundary}\r\n"
107
+ f'Content-Disposition: form-data; name="{name}"\r\n\r\n'
108
+ f"{value}\r\n"
109
+ ).encode("utf-8")
110
+
111
+ parts.append(field("model", model))
112
+ parts.append(field("prompt", prompt))
113
+ parts.append(field("n", str(count)))
114
+ parts.append(field("response_format", response_format))
115
+ if size:
116
+ parts.append(field("size", size))
117
+ if aspect_ratio:
118
+ parts.append(field("aspect_ratio", aspect_ratio))
119
+
120
+ # Image file
121
+ img_data = image_path.read_bytes()
122
+ img_mime = "image/jpeg" if image_path.suffix.lower() in (".jpg", ".jpeg") else "image/png"
123
+ parts.append(
124
+ (
125
+ f"--{boundary}\r\n"
126
+ f'Content-Disposition: form-data; name="image"; filename="{image_path.name}"\r\n'
127
+ f"Content-Type: {img_mime}\r\n\r\n"
128
+ ).encode("utf-8")
129
+ + img_data
130
+ + b"\r\n"
131
+ )
132
+
133
+ parts.append(f"--{boundary}--\r\n".encode("utf-8"))
134
+ body = b"".join(parts)
135
+
136
+ req = urllib.request.Request(
137
+ url, method="POST",
138
+ headers={
139
+ "Authorization": f"Bearer {api_key}",
140
+ "Content-Type": f"multipart/form-data; boundary={boundary}",
141
+ },
142
+ data=body,
143
+ )
144
+ try:
145
+ with urllib.request.urlopen(req, timeout=180) as resp:
146
+ return json.loads(resp.read().decode("utf-8"))
147
+ except urllib.error.HTTPError as e:
148
+ raise RuntimeError(f"VAPI API error ({e.code}): {e.read().decode('utf-8', errors='replace')}") from e
149
+
150
+
151
+ def process_response(data: list, response_format: str, should_save: bool,
152
+ out_dir: Path, prompt: str, count: int, filename: str) -> None:
153
+ for idx, item in enumerate(data):
154
+ image_url = item.get("url")
155
+ image_b64 = item.get("b64_json")
156
+
157
+ if response_format == "url" and image_url:
158
+ print(f"MEDIA:{image_url}")
159
+ continue
160
+
161
+ # Save mode: decode b64 or fallback to url download
162
+ if not image_b64 and image_url:
163
+ # API returned url even though we asked for b64 — download it
164
+ ts = datetime.now().strftime("%Y%m%d-%H%M%S")
165
+ fn = filename or f"{ts}-{slugify(prompt)}{f'-{idx+1}' if count > 1 else ''}.png"
166
+ fp = out_dir / fn
167
+ urllib.request.urlretrieve(image_url, fp)
168
+ elif image_b64:
169
+ ts = datetime.now().strftime("%Y%m%d-%H%M%S")
170
+ fn = filename or f"{ts}-{slugify(prompt)}{f'-{idx+1}' if count > 1 else ''}.png"
171
+ fp = out_dir / fn
172
+ fp.write_bytes(base64.b64decode(image_b64))
173
+ else:
174
+ print(f"Warning: no data for image {idx+1}", file=sys.stderr)
175
+ continue
176
+
177
+ full_path = fp.resolve()
178
+ print(f"Image saved: {full_path}", file=sys.stderr)
179
+ print(f"MEDIA:{full_path}")
180
+
181
+
182
+ def main():
183
+ ap = argparse.ArgumentParser(description="Generate or edit images via VAPI API.")
184
+ ap.add_argument("--prompt", required=True, help="Image prompt or edit instruction.")
185
+ ap.add_argument("--model", default="nano-banana-pro", help="Model name.")
186
+ ap.add_argument("--input", default="", help="Input image path or URL for editing (enables edit mode).")
187
+ ap.add_argument("--size", default="", help="Image size (e.g. 1024x1024).")
188
+ ap.add_argument("--aspect-ratio", default="", help="Aspect ratio for generation (e.g. 3:4, 16:9).")
189
+ ap.add_argument("--count", type=int, default=1, help="Number of images.")
190
+ ap.add_argument("--save", action="store_true", help="Save to ~/.openclaw/media/.")
191
+ ap.add_argument("--oss", action="store_true", help="Save to ~/.openclaw/oss/.")
192
+ ap.add_argument("--out-dir", default="", help="Save to custom directory.")
193
+ ap.add_argument("--filename", default="", help="Custom output filename.")
194
+ args = ap.parse_args()
195
+
196
+ api_key = os.environ.get("VAPI_API_KEY", "").strip()
197
+ base_url = os.environ.get("VAPI_BASE_URL", "https://api.v3.cm/v1").strip()
198
+ if not api_key:
199
+ print("Error: VAPI_API_KEY not set.", file=sys.stderr); sys.exit(1)
200
+ if not base_url:
201
+ base_url = "https://api.v3.cm/v1"
202
+
203
+ is_gpt_image = args.model.startswith("gpt-image")
204
+ should_save = args.save or args.oss or is_gpt_image
205
+ response_format = "b64_json" if should_save else "url"
206
+
207
+ if should_save:
208
+ if args.out_dir:
209
+ out_dir = Path(args.out_dir)
210
+ elif args.oss:
211
+ out_dir = get_oss_dir()
212
+ else:
213
+ out_dir = get_default_save_dir()
214
+ out_dir.mkdir(parents=True, exist_ok=True)
215
+ else:
216
+ out_dir = get_default_save_dir() # unused but needed for process_response signature
217
+
218
+ try:
219
+ if args.input:
220
+ # Edit mode
221
+ input_path = args.input.strip()
222
+ if input_path.startswith("http"):
223
+ print(f"Downloading input image...", file=sys.stderr)
224
+ local_input = download_to_temp(input_path)
225
+ else:
226
+ local_input = Path(input_path)
227
+
228
+ print(f"Editing image with model={args.model} format={response_format}...", file=sys.stderr)
229
+ response = request_edit(
230
+ base_url=base_url, api_key=api_key,
231
+ prompt=args.prompt, model=args.model,
232
+ image_path=local_input, response_format=response_format,
233
+ size=args.size, aspect_ratio=args.aspect_ratio,
234
+ count=args.count,
235
+ )
236
+ else:
237
+ # Generate mode
238
+ print(f"Generating with model={args.model} format={response_format}...", file=sys.stderr)
239
+ response = request_generate(
240
+ base_url=base_url, api_key=api_key,
241
+ prompt=args.prompt, model=args.model,
242
+ response_format=response_format,
243
+ size=args.size, aspect_ratio=args.aspect_ratio,
244
+ count=args.count,
245
+ )
246
+
247
+ data = response.get("data", [])
248
+ if not data:
249
+ print("Error: No image data in response.", file=sys.stderr); sys.exit(1)
250
+
251
+ process_response(data, response_format, should_save, out_dir,
252
+ args.prompt, args.count, args.filename)
253
+
254
+ except Exception as e:
255
+ print(f"Error: {e}", file=sys.stderr); sys.exit(1)
256
+
257
+
258
+ if __name__ == "__main__":
259
+ main()
@@ -0,0 +1,74 @@
1
+ ---
2
+ name: yiran-skill-media
3
+ description: 统一多媒体生成技能。支持图片和音乐生成,按资源类型自动路由到最优 provider,支持主备切换。生成文件统一保存到 workspace 的 media/ 目录。
4
+ ---
5
+
6
+ # 统一多媒体生成
7
+
8
+ ## 环境变量
9
+
10
+ | 变量 | 默认值 | 说明 |
11
+ |------|--------|------|
12
+ | `WORKSPACE_NAME` | `main` | workspace 名称 |
13
+ | `VAPI_API_KEY` | - | VAPI 图片 API Key(主 provider) |
14
+
15
+ ## 输出路径规则
16
+
17
+ - **main** workspace → `/root/.openclaw/workspace/media/`
18
+ - 其他 workspace → `/root/.openclaw/workspace-{name}/media/`
19
+ - 文件名:`image_{时间戳}.png` / `music_{时间戳}.mp3`
20
+
21
+ ## 一键脚本
22
+
23
+ ```bash
24
+ # 图片生成
25
+ WORKSPACE_NAME=main ./image.sh "描述" [--aspect-ratio 16:9] [--registry]
26
+
27
+ # 音乐生成
28
+ WORKSPACE_NAME=main ./music.sh "描述" [--lyrics "歌词"] [--instrumental] [--registry]
29
+ ```
30
+
31
+ ## 参数说明
32
+
33
+ ### image.sh
34
+
35
+ | 参数 | 必填 | 说明 |
36
+ |------|------|------|
37
+ | `prompt` | 是 | 图片描述 |
38
+ | `--aspect-ratio` | 否 | 比例,默认 1:1。可选:16:9, 9:16, 4:3 等 |
39
+ | `--registry` | 否 | 生成后注册到 `__MY_ARTIFACTS__.json` |
40
+
41
+ ### music.sh
42
+
43
+ | 参数 | 必填 | 说明 |
44
+ |------|------|------|
45
+ | `prompt` | 是 | 音乐风格/情绪描述 |
46
+ | `--lyrics` | 否 | 歌词文本 |
47
+ | `--instrumental` | 否 | 纯音乐模式 |
48
+ | `--registry` | 否 | 生成后注册到 `__MY_ARTIFACTS__.json` |
49
+
50
+ ## 架构
51
+
52
+ ```
53
+ image.sh / music.sh → 智能体调用的薄壳入口
54
+ generate.py → 统一路由调度(主备切换)
55
+ config.json → provider 配置中心(key、模型、地址)
56
+ providers/ → 各 provider 适配器
57
+ vapi_image.py → VAPI 图片(主)
58
+ minimax_image.py → MiniMax 图片(备)
59
+ minimax_music.py → MiniMax 音乐(主)
60
+ registry.py → 资源注册到 __MY_ARTIFACTS__.json
61
+ ```
62
+
63
+ ## Provider 配置
64
+
65
+ 编辑 `config.json` 可切换主备 provider、更换模型或 API Key。
66
+
67
+ 当前配置:
68
+ - **图片**:VAPI (nano-banana-pro) → fallback MiniMax (image-01)
69
+ - **音乐**:MiniMax (music-2.6)
70
+
71
+ ## 详细 API 参考
72
+
73
+ - [references/image-api.md](references/image-api.md)
74
+ - [references/music-api.md](references/music-api.md)
@@ -0,0 +1,26 @@
1
+ {
2
+ "output_dir": "media",
3
+ "image": {
4
+ "primary": {
5
+ "provider": "minimax_image",
6
+ "model": "image-01",
7
+ "base_url": "https://api.minimaxi.com/v1",
8
+ "api_key": "sk-cp-DC5lWd2Stt9CBFzLIT2awP4K-ZEn5AkYwjl3Cdj-mIBmgjxod518F2LaVF2L9c35Wv5-Eox0F1ctJD5vXtB9p3OmxoWLd9ge9zIUIMrCVuqBYdL_s6kb8Qs"
9
+ },
10
+ "fallback": {
11
+ "provider": "vapi_image",
12
+ "model": "nano-banana-pro",
13
+ "base_url": "https://api.v3.cm/v1",
14
+ "api_key": "sk-PXPUzqllWKJy2oj011Df510242264219Ba21093e3d2b2335"
15
+ }
16
+ },
17
+ "music": {
18
+ "primary": {
19
+ "provider": "minimax_music",
20
+ "model": "music-2.6",
21
+ "base_url": "https://api.minimaxi.com/v1",
22
+ "api_key": "sk-cp-DC5lWd2Stt9CBFzLIT2awP4K-ZEn5AkYwjl3Cdj-mIBmgjxod518F2LaVF2L9c35Wv5-Eox0F1ctJD5vXtB9p3OmxoWLd9ge9zIUIMrCVuqBYdL_s6kb8Qs"
23
+ },
24
+ "fallback": null
25
+ }
26
+ }
@@ -0,0 +1,88 @@
1
+ # MiniMax 文生图 API 参考
2
+
3
+ ## 端点
4
+ ```
5
+ POST https://api.minimaxi.com/v1/image_generation
6
+ ```
7
+
8
+ ## 请求头
9
+ ```
10
+ Authorization: Bearer <API_KEY>
11
+ Content-Type: application/json
12
+ ```
13
+
14
+ ## 请求体参数
15
+
16
+ | 参数 | 类型 | 必填 | 说明 |
17
+ |------|------|------|------|
18
+ | `model` | string | ✅ | `image-01` 或 `image-01-live` |
19
+ | `prompt` | string | ✅ | 图像描述,最长 1500 字符 |
20
+ | `aspect_ratio` | string | ❌ | `1:1` `16:9` `4:3` `3:2` `2:3` `3:4` `9:16` `21:9` 默认 `1:1` |
21
+ | `response_format` | string | ❌ | `url` 或 `base64`,默认 `url`(24小时有效) |
22
+ | `n` | integer | ❌ | 生成数量 1-9,默认 1 |
23
+ | `seed` | integer | ❌ | 随机种子,用于复现 |
24
+ | `prompt_optimizer` | boolean | ❌ | 自动优化 prompt,默认 false |
25
+ | `aigc_watermark` | boolean | ❌ | 添加水印,默认 false |
26
+ | `style` | object | ❌ | 仅 `image-01-live` 支持,画风设置 |
27
+
28
+ ### style 画风参数(仅 image-01-live)
29
+ ```json
30
+ {
31
+ "style_type": "漫画|元气|中世纪|水彩",
32
+ "style_weight": 0.8
33
+ }
34
+ ```
35
+
36
+ ## 响应
37
+ ```json
38
+ {
39
+ "id": "任务ID",
40
+ "data": {
41
+ "image_urls": ["https://..."], // response_format=url
42
+ "image_base64": ["base64..."] // response_format=base64
43
+ },
44
+ "metadata": {
45
+ "success_count": 1,
46
+ "failed_count": 0
47
+ },
48
+ "base_resp": { "status_code": 0, "status_msg": "success" }
49
+ }
50
+ ```
51
+
52
+ ## 错误码
53
+ - `0` — 成功
54
+ - `1002` — 限流
55
+ - `1004` — 鉴权失败
56
+ - `1008` — 余额不足
57
+ - `1026` — 内容涉敏
58
+ - `2013` — 参数错误
59
+ - `2049` — 无效 key
60
+ - `2061` — 模型不支持(key 无权限)
61
+
62
+ ## 常用调用示例
63
+
64
+ ### curl(base64)
65
+ ```bash
66
+ curl -X POST https://api.minimaxi.com/v1/image_generation \
67
+ -H "Authorization: Bearer $MINIMAX_API_KEY" \
68
+ -H "Content-Type: application/json" \
69
+ -d '{
70
+ "model": "image-01",
71
+ "prompt": "sci-fi mecha robot, neon lights",
72
+ "aspect_ratio": "16:9",
73
+ "response_format": "base64"
74
+ }'
75
+ ```
76
+
77
+ ### curl(url)
78
+ ```bash
79
+ curl -X POST https://api.minimaxi.com/v1/image_generation \
80
+ -H "Authorization: Bearer $MINIMAX_API_KEY" \
81
+ -H "Content-Type: application/json" \
82
+ -d '{
83
+ "model": "image-01",
84
+ "prompt": "sci-fi mecha robot, neon lights",
85
+ "aspect_ratio": "16:9",
86
+ "response_format": "url"
87
+ }'
88
+ ```
@@ -0,0 +1,120 @@
1
+ # MiniMax 音乐生成 API 参考
2
+
3
+ ## 端点
4
+ ```
5
+ POST https://api.minimaxi.com/v1/music_generation
6
+ ```
7
+
8
+ ## 请求头
9
+ ```
10
+ Authorization: Bearer <API_KEY>
11
+ Content-Type: application/json
12
+ ```
13
+
14
+ ## 请求体参数
15
+
16
+ | 参数 | 类型 | 必填 | 说明 |
17
+ |------|------|------|------|
18
+ | `model` | string | ✅ | `music-2.6`, `music-cover`, `music-2.6-free`, `music-cover-free` |
19
+ | `prompt` | string | ✅/❌ | 音乐描述,1-2000字符。纯音乐时必填 |
20
+ | `lyrics` | string | ✅/❌ | 歌词,`\n`分隔,支持结构标签如 `[Verse]`, `[Chorus]` 等 |
21
+ | `is_instrumental` | boolean | ❌ | 是否纯音乐(无人声),默认 false。true 时 lyrics 非必填 |
22
+ | `output_format` | string | ❌ | `url` 或 `hex`,默认 `hex`(24小时有效) |
23
+ | `stream` | boolean | ❌ | 流式传输,默认 false |
24
+ | `lyrics_optimizer` | boolean | ❌ | 自动生成歌词,仅 music-2.6 系列 |
25
+ | `aigc_watermark` | boolean | ❌ | 添加水印,默认 false |
26
+ | `audio_setting` | object | ❌ | 采样率/比特率/格式设置 |
27
+
28
+ ### audio_setting 参数
29
+ ```json
30
+ {
31
+ "sample_rate": 44100, // 16000, 24000, 32000, 44100
32
+ "bitrate": 256000, // 32000, 64000, 128000, 256000
33
+ "format": "mp3" // mp3, wav, pcm
34
+ }
35
+ ```
36
+
37
+ ### 歌词结构标签
38
+ `[Intro]`, `[Verse]`, `[Pre Chorus]`, `[Chorus]`, `[Interlude]`, `[Bridge]`, `[Outro]`, `[Post Chorus]`, `[Transition]`, `[Break]`, `[Hook]`, `[Build Up]`, `[Inst]`, `[Solo]`
39
+
40
+ ## 响应
41
+ ```json
42
+ {
43
+ "data": {
44
+ "audio": "hex编码...",
45
+ "status": 2 // 1=合成中, 2=已完成
46
+ },
47
+ "extra_info": {
48
+ "music_duration": 25364, // 毫秒
49
+ "music_sample_rate": 44100,
50
+ "music_channel": 2,
51
+ "bitrate": 256000,
52
+ "music_size": 813651
53
+ },
54
+ "base_resp": { "status_code": 0, "status_msg": "success" }
55
+ }
56
+ ```
57
+
58
+ ## 错误码
59
+ - `0` — 成功
60
+ - `1002` — 限流
61
+ - `1004` — 鉴权失败
62
+ - `1008` — 余额不足
63
+ - `1026` — 内容涉敏
64
+ - `2013` — 参数错误
65
+ - `2049` — 无效 key
66
+ - `2061` — 模型不支持
67
+
68
+ ## 常用调用示例
69
+
70
+ ### curl(纯音乐,hex)
71
+ ```bash
72
+ curl -X POST https://api.minimaxi.com/v1/music_generation \
73
+ -H "Authorization: Bearer $MINIMAX_API_KEY" \
74
+ -H "Content-Type: application/json" \
75
+ -d '{
76
+ "model": "music-2.6",
77
+ "prompt": "acoustic guitar instrumental, relaxing, peaceful",
78
+ "is_instrumental": true,
79
+ "output_format": "hex"
80
+ }'
81
+ ```
82
+
83
+ ### curl(非纯音乐,带歌词,url)
84
+ ```bash
85
+ curl -X POST https://api.minimaxi.com/v1/music_generation \
86
+ -H "Authorization: Bearer $MINIMAX_API_KEY" \
87
+ -H "Content-Type: application/json" \
88
+ -d '{
89
+ "model": "music-2.6",
90
+ "prompt": "流行音乐, 忧郁, 适合在下雨的晚上",
91
+ "lyrics": "[Verse]\n街灯微亮晚风轻抚\n影子拉长独自漫步\n\n[Chorus]\n推开木门香气弥漫",
92
+ "output_format": "url",
93
+ "audio_setting": {
94
+ "sample_rate": 44100,
95
+ "bitrate": 256000,
96
+ "format": "mp3"
97
+ }
98
+ }'
99
+ ```
100
+
101
+ ### hex 解码保存
102
+ ```python
103
+ import binascii, requests
104
+
105
+ resp = requests.post(endpoint, headers=headers, json=payload)
106
+ audio_hex = resp.json()["data"]["audio"]
107
+ with open("output.mp3", "wb") as f:
108
+ f.write(binascii.unhexlify(audio_hex))
109
+ ```
110
+
111
+ ### url 下载保存
112
+ ```python
113
+ import requests
114
+
115
+ resp = requests.post(endpoint, headers=headers, json=payload)
116
+ url = resp.json()["data"]["audio"] # 24小时内有效
117
+ r = requests.get(url)
118
+ with open("output.mp3", "wb") as f:
119
+ f.write(r.content)
120
+ ```