handle-media
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseWhat This Skill Does
该Skill的功能
A lightweight Layer 1 atomic engine that resolves any image source into a Sivi media reference ( + ). No brand required, no save-to-folder. Composite skills call this instead of inlining upload/create-media/generate scripts.
mIdmediaUrl4 input sources:
- Local file → presigned upload → → returns
create-media+mIdmediaUrl - Direct image URL → with
create-media→ returnsurl+mIdmediaUrl - Product/webpage URL → with
create-media(auto-pick) → returnsurl+mIdmediaUrl - AI generation → → poll → returns
generate(nomediaUrl)mId
Output for the calling skill:
- Sources 1–3: → use in
mId;siviAssets[]→ use inmediaUrlif preferredassets.images[] - Source 4: → use in
mediaUrlassets.images[] - Source 2 can also be used directly in without calling this skill — only call if you need an
assets.images[]mId
这是一个轻量级的Layer 1原子引擎,可将任意图片源解析为Sivi媒体引用( + )。无需关联品牌,无需保存到文件夹。复合Skill会调用该工具,而非自行编写上传/create-media/生成脚本。
mIdmediaUrl4种输入源:
- 本地文件 → 预签名上传 → → 返回
create-media+mIdmediaUrl - 直接图片URL → 带参数调用
url→ 返回create-media+mIdmediaUrl - 产品/网页URL → 带参数调用
url(自动提取) → 返回create-media+mIdmediaUrl - AI生成 → → 轮询 → 返回
generate(无mediaUrl)mId
给调用方Skill的输出:
- 源1–3:→ 用于
mId;siviAssets[]→ 如需也可用于mediaUrlassets.images[] - 源4:→ 用于
mediaUrlassets.images[] - 源2的URL也可直接用于,无需调用该Skill——仅当需要
assets.images[]时才调用mId
⚠️ Cross-Platform Compatibility — MANDATORY
⚠️ 跨平台兼容性——强制要求
- NEVER use or
head -n -1. Usejqfor JSON parsing.python3 - ALWAYS use to separate HTTP status from response body.
curl -o <file> -w '%{http_code}' - Temp files: Use .
/tmp/
- **切勿使用**或
head -n -1。使用jq解析JSON。python3 - 始终使用分离HTTP状态码与响应体。
curl -o <file> -w '%{http_code}' - 临时文件:使用目录。
/tmp/
Steps
步骤
1. Identify the input source
1. 识别输入源
Determine which of the 4 sources the input is:
- Local file — input is a file path that exists on the local filesystem (e.g., ,
/Users/user/photo.jpg)./images/logo.png - Direct image URL — input is a URL ending in an image extension (,
.jpg,.jpeg,.png,.gif,.webp) or explicitly described as a direct image link.svg - Product/webpage URL — input is a URL that does NOT end in an image extension but is a page containing images (e.g., ,
https://shop.example.com/product)https://example.com/collection - AI generation — input is a text prompt describing an image to generate, or the user explicitly asks for AI-generated images
If unclear, ask the user: "Is this a local file, a direct image URL, a product/webpage URL to extract images from, or should I generate an image with AI?"
判断输入属于以下4种源中的哪一种:
- 本地文件 — 输入为本地文件系统中存在的文件路径(例如:、
/Users/user/photo.jpg)./images/logo.png - 直接图片URL — 输入为以图片扩展名结尾的URL(、
.jpg、.jpeg、.png、.gif、.webp),或明确描述为直接图片链接.svg - 产品/网页URL — 输入为不以图片扩展名结尾,但包含图片的页面URL(例如:、
https://shop.example.com/product)https://example.com/collection - AI生成 — 输入为描述要生成图片的文本提示词,或用户明确要求生成AI图片
若无法确定,请询问用户:"这是本地文件、直接图片URL、用于提取图片的产品/网页URL,还是需要通过AI生成图片?"
2. Classify the asset type
2. 分类资产类型
For all sources, determine the asset type:
- Logo → ,
type: "logo"— if the asset is a logo, brand mark, or monogramsubType: "logo" - Image → ,
type: "photo"— if the asset is a photo, illustration, product shot, or any non-logo visualsubType: "photograph" - Icon → ,
type: "photo"— simple graphic elements (treat as image for media registration)subType: "photograph"
Classification rules:
- If the filename/URL contains "logo", "brand", "mark" → logo
- If unsure → image (default)
对于所有源,确定资产类型:
- Logo → ,
type: "logo"— 若资产为Logo、品牌标识或字母组合subType: "logo" - 图片 → ,
type: "photo"— 若资产为照片、插画、产品图或任何非Logo类视觉内容subType: "photograph" - 图标 → ,
type: "photo"— 简单图形元素(注册媒体时视为图片)subType: "photograph"
分类规则:
- 若文件名/URL包含"logo"、"brand"、"mark" → Logo
- 若不确定 → 图片(默认)
3. Resolve the media
3. 解析媒体
Follow the path matching the identified source:
按照识别出的源执行对应流程:
Source 1: Local file upload
源1:本地文件上传
Three-step flow: get presigned URL → upload to S3 → register via create-media.
Determine file extension and content type:
- /
.jpg→.jpeg,extension: "jpeg"contentType: "image/jpeg" - →
.png,extension: "png"contentType: "image/png" - →
.webp,extension: "webp"contentType: "image/webp" - →
.svg,extension: "svg"contentType: "image/svg+xml" - →
.gif,extension: "gif"contentType: "image/gif"
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env三步流程:获取预签名URL → 上传至S3 → 通过create-media注册。
确定文件扩展名和内容类型:
- /
.jpg→.jpeg,extension: "jpeg"contentType: "image/jpeg" - →
.png,extension: "png"contentType: "image/png" - →
.webp,extension: "webp"contentType: "image/webp" - →
.svg,extension: "svg"contentType: "image/svg+xml" - →
.gif,extension: "gif"contentType: "image/gif"
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env✅ Replace placeholders below with actual values
✅ Replace placeholders below with actual values
LOCAL_FILE="<LOCAL_FILE_PATH>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
FILE_EXTENSION="<jpeg_OR_png_OR_webp_OR_svg_OR_gif>"
CONTENT_TYPE="<image/jpeg_OR_image/png_OR_etc>"
BRAND_ID="<BRAND_ID>"
LOCAL_FILE="<LOCAL_FILE_PATH>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
FILE_EXTENSION="<jpeg_OR_png_OR_webp_OR_svg_OR_gif>"
CONTENT_TYPE="<image/jpeg_OR_image/png_OR_etc>"
BRAND_ID="<BRAND_ID>"
Step 1: Get presigned URL
Step 1: Get presigned URL
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'extension': '$FILE_EXTENSION',
'contentType': '$CONTENT_TYPE'
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_presigned_response.json -w '%{http_code}'
-X POST "https://connect.sivi.ai/api/prod/v2/general/files/get-presigned-url"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
-X POST "https://connect.sivi.ai/api/prod/v2/general/files/get-presigned-url"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_presigned_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Presigned URL request failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
UPLOAD_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['uploadUrl'])" <<< "$BODY")
UPLOAD_CONTENT_TYPE=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['headers']['Content-Type'])" <<< "$BODY")
echo "Got presigned URL"
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'extension': '$FILE_EXTENSION',
'contentType': '$CONTENT_TYPE'
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_presigned_response.json -w '%{http_code}'
-X POST "https://connect.sivi.ai/api/prod/v2/general/files/get-presigned-url\"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
-X POST "https://connect.sivi.ai/api/prod/v2/general/files/get-presigned-url\"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_presigned_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Presigned URL request failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
UPLOAD_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['uploadUrl'])" <<< "$BODY")
UPLOAD_CONTENT_TYPE=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['headers']['Content-Type'])" <<< "$BODY")
echo "Got presigned URL"
Step 2: Upload file to presigned URL
Step 2: Upload file to presigned URL
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}'
-X PUT
-H "Content-Type: $UPLOAD_CONTENT_TYPE"
--data-binary "@$LOCAL_FILE"
"$UPLOAD_URL")
-X PUT
-H "Content-Type: $UPLOAD_CONTENT_TYPE"
--data-binary "@$LOCAL_FILE"
"$UPLOAD_URL")
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "204" ]; then
echo "ERROR: Upload to presigned URL failed with HTTP $HTTP_CODE"
exit 1
fi
echo "File uploaded to presigned URL"
HTTP_CODE=$(curl -s -o /dev/null -w '%{http_code}'
-X PUT
-H "Content-Type: $UPLOAD_CONTENT_TYPE"
--data-binary "@$LOCAL_FILE"
"$UPLOAD_URL")
-X PUT
-H "Content-Type: $UPLOAD_CONTENT_TYPE"
--data-binary "@$LOCAL_FILE"
"$UPLOAD_URL")
if [ "$HTTP_CODE" != "200" ] && [ "$HTTP_CODE" != "204" ]; then
echo "ERROR: Upload to presigned URL failed with HTTP $HTTP_CODE"
exit 1
fi
echo "File uploaded to presigned URL"
Step 3: Create media — register the uploaded file
Step 3: Create media — register the uploaded file
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'uploadUrl': '''$UPLOAD_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}'
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Create media failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< "$BODY")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< "$BODY")
echo "M_ID=$M_ID"
echo "MEDIA_URL=$MEDIA_URL"
---PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'uploadUrl': '''$UPLOAD_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}'
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create\"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create\"
-H "Content-Type: application/json"
-H "sivi-api-key: $SIVI_API_KEY"
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Create media failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< "$BODY")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< "$BODY")
echo "M_ID=$M_ID"
echo "MEDIA_URL=$MEDIA_URL"
---Source 2: Direct image URL
源2:直接图片URL
Call with the parameter. Sivi fetches and stores the image.
create-mediaurlbash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
IMAGE_URL="<DIRECT_IMAGE_URL>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'url': '''$IMAGE_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}' \
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create" \
-H "Content-Type: application/json" \
-H "sivi-api-key: $SIVI_API_KEY" \
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Create media failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< "$BODY")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< "$BODY")
echo "M_ID=$M_ID"
echo "MEDIA_URL=$MEDIA_URL"Note: If the calling skill only needs the URL for and does NOT need an , skip this API call entirely and use the direct image URL as-is. Only call this when an is needed for .
assets.images[]mIdmIdsiviAssets[]带参数调用。Sivi会获取并存储该图片。
urlcreate-mediabash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
IMAGE_URL="<DIRECT_IMAGE_URL>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'url': '''$IMAGE_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}' \
-X POST \"https://connect.sivi.ai/api/prod/v2/general/media/create\" \
-H \"Content-Type: application/json\" \
-H \"sivi-api-key: $SIVI_API_KEY\" \
-d \"$PAYLOAD\")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ \"$HTTP_CODE\" != \"200\" ]; then
echo \"ERROR: Create media failed with HTTP $HTTP_CODE\"
echo \"$BODY\"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< \"$BODY\")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< \"$BODY\")
echo \"M_ID=$M_ID\"
echo \"MEDIA_URL=$MEDIA_URL\"注意: 若调用方Skill仅需URL用于,不需要,则完全无需调用该API,直接使用原图片URL即可。仅当需要用于时才调用。
assets.images[]mIdmIdsiviAssets[]Source 3: Product/webpage URL auto-pick
源3:产品/网页URL自动提取
Call with the parameter set to the webpage URL. Sivi auto-picks relevant images from the page.
create-mediaurlbash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
WEBPAGE_URL="<PRODUCT_OR_WEBPAGE_URL>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'url': '''$WEBPAGE_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}' \
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/create" \
-H "Content-Type: application/json" \
-H "sivi-api-key: $SIVI_API_KEY" \
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: Create media failed with HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< "$BODY")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< "$BODY")
echo "M_ID=$M_ID"
echo "MEDIA_URL=$MEDIA_URL"将参数设为网页URL,调用。Sivi会自动从页面中提取相关图片。
urlcreate-mediabash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
WEBPAGE_URL="<PRODUCT_OR_WEBPAGE_URL>"
FILE_TYPE="<photo_OR_logo>"
FILE_SUBTYPE="<photograph_OR_logo>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'type': '$FILE_TYPE',
'subType': '$FILE_SUBTYPE',
'url': '''$WEBPAGE_URL'''
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_create_media_response.json -w '%{http_code}' \
-X POST \"https://connect.sivi.ai/api/prod/v2/general/media/create\" \
-H \"Content-Type: application/json\" \
-H \"sivi-api-key: $SIVI_API_KEY\" \
-d \"$PAYLOAD\")
BODY=$(cat /tmp/sivi_create_media_response.json)
if [ \"$HTTP_CODE\" != \"200\" ]; then
echo \"ERROR: Create media failed with HTTP $HTTP_CODE\"
echo \"$BODY\"
exit 1
fi
M_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['mId'])" <<< \"$BODY\")
MEDIA_URL=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['media']['url'])" <<< \"$BODY\")
echo \"M_ID=$M_ID\"
echo \"MEDIA_URL=$MEDIA_URL\"Source 4: AI generation
源4:AI生成
Call with a text prompt, then poll for the result.
generateSupported dimensions by model:
Choose dimensions from the table below before calling the generate API. Using unsupported dimensions will return a 422 error.
Nano Banana, Nano Banana 2, Nano Banana Pro, Nano Banana Lite:
| Width | Height | Aspect Ratio |
|---|---|---|
| 1264 | 848 | ~3:2 |
| 848 | 1264 | ~2:3 |
| 1200 | 896 | ~4:3 |
| 896 | 1200 | ~3:4 |
| 1152 | 928 | ~5:4 |
| 928 | 1152 | ~4:5 |
| 1376 | 768 | ~16:9 |
| 768 | 1376 | ~9:16 |
| 1548 | 672 | ~23:10 |
Z-Image Turbo
| Width | Height | Aspect Ratio |
|---|---|---|
| 1024 | 1024 | 1:1 |
| 1344 | 768 | 7:4 |
| 1280 | 960 | 4:3 |
| 960 | 1280 | 3:4 |
| 768 | 1344 | 4:7 |
Select the closest supported dimension matching the design's aspect ratio. For wide banners use 1344x768 (7:4) or 1376x768 (~16:9, nano-banana only). For vertical posters use 768x1344 (4:7) or 960x1280 (3:4). For square designs use 1024x1024.
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
PROMPT_TEXT="<IMAGE_GENERATION_PROMPT>"
WIDTH="<WIDTH>"
HEIGHT="<HEIGHT>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'prompt': '''$PROMPT_TEXT''',
'dimensions': {'width': $WIDTH, 'height': $HEIGHT},
'model': 'nano-banana-3-lite:1k',
'negativePrompt': 'no text, no letters, no words, no handwriting, no calligraphy, no labels, no titles, no signs, no logos, no watermarks.'
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_genmedia_response.json -w '%{http_code}' \
-X POST "https://connect.sivi.ai/api/prod/v2/general/media/generate" \
-H "Content-Type: application/json" \
-H "sivi-api-key: $SIVI_API_KEY" \
-d "$PAYLOAD")
BODY=$(cat /tmp/sivi_genmedia_response.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
REQUEST_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['requestId'])" <<< "$BODY")
echo "REQUEST_ID=$REQUEST_ID"Then poll for the result:
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
REQUEST_ID="<REQUEST_ID_FROM_ABOVE>"
MAX_ATTEMPTS=30
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
ATTEMPT=$((ATTEMPT + 1))
echo "Poll attempt $ATTEMPT/$MAX_ATTEMPTS..."
QUERY_PARAMS=$(python3 -c "
import json, urllib.parse
params = json.dumps({'requestId': '''$REQUEST_ID'''})
print(urllib.parse.quote(params, safe=''))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_media_status.json -w '%{http_code}' \
-X GET "https://connect.sivi.ai/api/prod/v2/general/get-request-status?queryParams=$QUERY_PARAMS" \
-H "sivi-api-key: $SIVI_API_KEY")
BODY=$(cat /tmp/sivi_media_status.json)
if [ "$HTTP_CODE" != "200" ]; then
echo "ERROR: HTTP $HTTP_CODE"
echo "$BODY"
exit 1
fi
STATUS=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['status'])" <<< "$BODY")
if [ "$STATUS" = "completed" ]; then
echo "MEDIA GENERATION COMPLETE"
python3 -c "
import json, sys
d = json.load(sys.stdin)
result = d['body'].get('result', {})
media = result.get('media', {})
if media:
print(f\"MEDIA_URL={media.get('url', '')}\")
print(f\"MEDIA_ID={media.get('mId', '')}\")
else:
print(f\"RESULT={json.dumps(result)}\")
" <<< "$BODY"
exit 0
elif [ "$STATUS" = "failed" ] || [ "$STATUS" = "suspended" ]; then
echo "MEDIA GENERATION FAILED ($STATUS)"
echo "$BODY"
exit 1
fi
sleep 10
done
echo "TIMEOUT"
exit 1带文本提示词调用,然后轮询获取结果。
generate各模型支持的尺寸:
调用生成API前,请从下表选择尺寸。使用不支持的尺寸会返回422错误。
Nano Banana、Nano Banana 2、Nano Banana Pro、Nano Banana Lite:
| 宽度 | 高度 | 宽高比 |
|---|---|---|
| 1264 | 848 | ~3:2 |
| 848 | 1264 | ~2:3 |
| 1200 | 896 | ~4:3 |
| 896 | 1200 | ~3:4 |
| 1152 | 928 | ~5:4 |
| 928 | 1152 | ~4:5 |
| 1376 | 768 | ~16:9 |
| 768 | 1376 | ~9:16 |
| 1548 | 672 | ~23:10 |
Z-Image Turbo
| 宽度 | 高度 | 宽高比 |
|---|---|---|
| 1024 | 1024 | 1:1 |
| 1344 | 768 | 7:4 |
| 1280 | 960 | 4:3 |
| 960 | 1280 | 3:4 |
| 768 | 1344 | 4:7 |
选择与设计宽高比最接近的支持尺寸。宽幅横幅使用1344x768(7:4)或1376x768(~16:9,仅Nano Banana支持)。竖版海报使用768x1344(4:7)或960x1280(3:4)。方形设计使用1024x1024。
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
PROMPT_TEXT="<IMAGE_GENERATION_PROMPT>"
WIDTH="<WIDTH>"
HEIGHT="<HEIGHT>"
BRAND_ID="<BRAND_ID>"
PAYLOAD=$(python3 -c "
import json
data = {
'prompt': '''$PROMPT_TEXT''',
'dimensions': {'width': $WIDTH, 'height': $HEIGHT},
'model': 'nano-banana-3-lite:1k',
'negativePrompt': 'no text, no letters, no words, no handwriting, no calligraphy, no labels, no titles, no signs, no logos, no watermarks.'
}
if '$BRAND_ID':
data['bId'] = '$BRAND_ID'
print(json.dumps(data))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_genmedia_response.json -w '%{http_code}' \
-X POST \"https://connect.sivi.ai/api/prod/v2/general/media/generate\" \
-H \"Content-Type: application/json\" \
-H \"sivi-api-key: $SIVI_API_KEY\" \
-d \"$PAYLOAD\")
BODY=$(cat /tmp/sivi_genmedia_response.json)
if [ \"$HTTP_CODE\" != \"200\" ]; then
echo \"ERROR: HTTP $HTTP_CODE\"
echo \"$BODY\"
exit 1
fi
REQUEST_ID=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['requestId'])" <<< \"$BODY\")
echo \"REQUEST_ID=$REQUEST_ID\"然后轮询获取结果:
bash
#!/bin/bash
set -e
source <SKILL_REPO>/.env
REQUEST_ID="<REQUEST_ID_FROM_ABOVE>"
MAX_ATTEMPTS=30
ATTEMPT=0
while [ $ATTEMPT -lt $MAX_ATTEMPTS ]; do
ATTEMPT=$((ATTEMPT + 1))
echo \"Poll attempt $ATTEMPT/$MAX_ATTEMPTS...\"
QUERY_PARAMS=$(python3 -c "
import json, urllib.parse
params = json.dumps({'requestId': '''$REQUEST_ID'''})
print(urllib.parse.quote(params, safe=''))
")
HTTP_CODE=$(curl -s -o /tmp/sivi_media_status.json -w '%{http_code}' \
-X GET \"https://connect.sivi.ai/api/prod/v2/general/get-request-status?queryParams=$QUERY_PARAMS\" \
-H \"sivi-api-key: $SIVI_API_KEY\")
BODY=$(cat /tmp/sivi_media_status.json)
if [ \"$HTTP_CODE\" != \"200\" ]; then
echo \"ERROR: HTTP $HTTP_CODE\"
echo \"$BODY\"
exit 1
fi
STATUS=$(python3 -c "import json,sys; d=json.load(sys.stdin); print(d['body']['status'])" <<< \"$BODY\")
if [ \"$STATUS\" = \"completed\" ]; then
echo \"MEDIA GENERATION COMPLETE\"
python3 -c "
import json, sys
d = json.load(sys.stdin)
result = d['body'].get('result', {})
media = result.get('media', {})
if media:
print(f\"MEDIA_URL={media.get('url', '')}\")
print(f\"MEDIA_ID={media.get('mId', '')}\")
else:
print(f\"RESULT={json.dumps(result)}\")
" <<< \"$BODY\"
exit 0
elif [ \"$STATUS\" = \"failed\" ] || [ \"$STATUS\" = \"suspended\" ]; then
echo \"MEDIA GENERATION FAILED ($STATUS)\"
echo \"$BODY\"
exit 1
fi
sleep 10
done
echo \"TIMEOUT\"
exit 14. Return result to calling skill
4. 向调用方Skill返回结果
After the script completes, return:
- — Sivi media ID (sources 1–3). Use in
M_IDin the design payload.siviAssets[] - — public media URL (all sources). Use in
MEDIA_URLin the design payload.assets.images[]
For AI generation (source 4), only is returned (no ).
MEDIA_URLmId脚本完成后,返回:
- — Sivi媒体ID(源1–3)。用于设计请求体中的
M_ID。siviAssets[] - — 公开媒体URL(所有源)。用于设计请求体中的
MEDIA_URL。assets.images[]
对于AI生成(源4),仅返回(无)。
MEDIA_URLmId5. Handle errors
5. 错误处理
- On 401: "Your SIVI_API_KEY is missing or invalid."
- On 402: "Insufficient Sivi credits."
- On 400: "Uploaded file not found in S3. Ensure the upload completed successfully."
- On 422: "Invalid input: <error>. Check the URL, file type, or parameters."
- On 500: "Sivi server errored. Please retry."
- 401错误:"你的SIVI_API_KEY缺失或无效。"
- 402错误:"Sivi积分不足。"
- 400错误:"上传的文件在S3中未找到。请确保上传已成功完成。"
- 422错误:"无效输入:<错误信息>。请检查URL、文件类型或参数。"
- 500错误:"Sivi服务器出错。请重试。"
Notes
注意事项
- This skill is a utility for other skills — it is not typically invoked directly by users.
- Brand association is optional — is not required by the API. If a brand was matched, pass its ID. If no brand was matched, omit
bIdentirely.bId - No files are saved to the folder. For brand-scoped asset management with folder saving, use
brands/(coming soon).brand-assets - Maximum of 4 image assets total (combined +
siviAssets) when using the generate API.assets - For standalone AI image enhancement (not generation), use with
enhance-media.model: "nano-banana:1k" - Direct image URLs (source 2) can be used directly in without calling this skill. Only call
assets.images[]when ancreate-mediais needed formId.siviAssets[] - The API docs: https://developer.sivi.ai/docs/sivi-api/core-api/media/create-media
create-media
- 该Skill是供其他Skill使用的工具——通常不直接由用户调用。
- 品牌关联是可选的——API不需要。若已匹配到品牌,则传入其ID;若未匹配到品牌,则完全省略
bId。bId - 文件不会保存到文件夹。如需支持文件夹存储的品牌级资产管理,请使用
brands/(即将推出)。brand-assets - 使用生成API时,+
siviAssets总计最多支持4个图片资产。assets - 如需独立的AI图片增强(而非生成)功能,请使用并指定
enhance-media。model: "nano-banana:1k" - 直接图片URL(源2)可直接用于,无需调用该Skill。仅当需要
assets.images[]用于mId时才调用siviAssets[]。create-media - API文档:https://developer.sivi.ai/docs/sivi-api/core-api/media/create-media
create-media