handle-media

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

What This Skill Does

该Skill的功能

A lightweight Layer 1 atomic engine that resolves any image source into a Sivi media reference (
mId
+
mediaUrl
). No brand required, no save-to-folder. Composite skills call this instead of inlining upload/create-media/generate scripts.
4 input sources:
  1. Local file → presigned upload →
    create-media
    → returns
    mId
    +
    mediaUrl
  2. Direct image URL
    create-media
    with
    url
    → returns
    mId
    +
    mediaUrl
  3. Product/webpage URL
    create-media
    with
    url
    (auto-pick) → returns
    mId
    +
    mediaUrl
  4. AI generation
    generate
    → poll → returns
    mediaUrl
    (no
    mId
    )
Output for the calling skill:
  • Sources 1–3:
    mId
    → use in
    siviAssets[]
    ;
    mediaUrl
    → use in
    assets.images[]
    if preferred
  • Source 4:
    mediaUrl
    → use in
    assets.images[]
  • Source 2 can also be used directly in
    assets.images[]
    without calling this skill — only call if you need an
    mId
这是一个轻量级的Layer 1原子引擎,可将任意图片源解析为Sivi媒体引用(
mId
+
mediaUrl
)。无需关联品牌,无需保存到文件夹。复合Skill会调用该工具,而非自行编写上传/create-media/生成脚本。
4种输入源:
  1. 本地文件 → 预签名上传 →
    create-media
    → 返回
    mId
    +
    mediaUrl
  2. 直接图片URL → 带
    url
    参数调用
    create-media
    → 返回
    mId
    +
    mediaUrl
  3. 产品/网页URL → 带
    url
    参数调用
    create-media
    (自动提取) → 返回
    mId
    +
    mediaUrl
  4. AI生成
    generate
    → 轮询 → 返回
    mediaUrl
    (无
    mId
给调用方Skill的输出:
  • 源1–3:
    mId
    → 用于
    siviAssets[]
    mediaUrl
    → 如需也可用于
    assets.images[]
  • 源4:
    mediaUrl
    → 用于
    assets.images[]
  • 源2的URL也可直接用于
    assets.images[]
    ,无需调用该Skill——仅当需要
    mId
    时才调用

⚠️ Cross-Platform Compatibility — MANDATORY

⚠️ 跨平台兼容性——强制要求

  • NEVER use
    head -n -1
    or
    jq
    . Use
    python3
    for JSON parsing.
  • ALWAYS use
    curl -o <file> -w '%{http_code}'
    to separate HTTP status from response body.
  • Temp files: Use
    /tmp/
    .
  • **切勿使用
    head -n -1
    **或
    jq
    。使用
    python3
    解析JSON。
  • 始终使用
    curl -o <file> -w '%{http_code}'
    分离HTTP状态码与响应体。
  • 临时文件:使用
    /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
    ,
    .svg
    ) or explicitly described as a direct image link
  • 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"
    ,
    subType: "logo"
    — if the asset is a logo, brand mark, or monogram
  • Image
    type: "photo"
    ,
    subType: "photograph"
    — if the asset is a photo, illustration, product shot, or any non-logo visual
  • Icon
    type: "photo"
    ,
    subType: "photograph"
    — simple graphic elements (treat as image for media registration)
Classification rules:
  • If the filename/URL contains "logo", "brand", "mark" → logo
  • If unsure → image (default)
对于所有源,确定资产类型:
  • Logo
    type: "logo"
    subType: "logo"
    — 若资产为Logo、品牌标识或字母组合
  • 图片
    type: "photo"
    subType: "photograph"
    — 若资产为照片、插画、产品图或任何非Logo类视觉内容
  • 图标
    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")
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")
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")
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")
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")
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")
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
create-media
with the
url
parameter. Sivi fetches and stores the image.
bash
#!/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
assets.images[]
and does NOT need an
mId
, skip this API call entirely and use the direct image URL as-is. Only call this when an
mId
is needed for
siviAssets[]
.

url
参数调用
create-media
。Sivi会获取并存储该图片。
bash
#!/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用于
assets.images[]
,不需要
mId
,则完全无需调用该API,直接使用原图片URL即可。仅当需要
mId
用于
siviAssets[]
时才调用。

Source 3: Product/webpage URL auto-pick

源3:产品/网页URL自动提取

Call
create-media
with the
url
parameter set to the webpage URL. Sivi auto-picks relevant images from the page.
bash
#!/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
参数设为网页URL,调用
create-media
。Sivi会自动从页面中提取相关图片。
bash
#!/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
generate
with a text prompt, then poll for the result.
Supported 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:
WidthHeightAspect Ratio
1264848~3:2
8481264~2:3
1200896~4:3
8961200~3:4
1152928~5:4
9281152~4:5
1376768~16:9
7681376~9:16
1548672~23:10
Z-Image Turbo
WidthHeightAspect Ratio
102410241:1
13447687:4
12809604:3
96012803:4
76813444: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:
宽度高度宽高比
1264848~3:2
8481264~2:3
1200896~4:3
8961200~3:4
1152928~5:4
9281152~4:5
1376768~16:9
7681376~9:16
1548672~23:10
Z-Image Turbo
宽度高度宽高比
102410241:1
13447687:4
12809604:3
96012803:4
76813444: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 1

4. Return result to calling skill

4. 向调用方Skill返回结果

After the script completes, return:
  • M_ID
    — Sivi media ID (sources 1–3). Use in
    siviAssets[]
    in the design payload.
  • MEDIA_URL
    — public media URL (all sources). Use in
    assets.images[]
    in the design payload.
For AI generation (source 4), only
MEDIA_URL
is returned (no
mId
).
脚本完成后,返回:
  • M_ID
    — Sivi媒体ID(源1–3)。用于设计请求体中的
    siviAssets[]
  • MEDIA_URL
    — 公开媒体URL(所有源)。用于设计请求体中的
    assets.images[]
对于AI生成(源4),仅返回
MEDIA_URL
(无
mId
)。

5. 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
    bId
    is not required by the API. If a brand was matched, pass its ID. If no brand was matched, omit
    bId
    entirely.
  • No files are saved to the
    brands/
    folder. For brand-scoped asset management with folder saving, use
    brand-assets
    (coming soon).
  • Maximum of 4 image assets total (combined
    siviAssets
    +
    assets
    ) when using the generate API.
  • For standalone AI image enhancement (not generation), use
    enhance-media
    with
    model: "nano-banana:1k"
    .
  • Direct image URLs (source 2) can be used directly in
    assets.images[]
    without calling this skill. Only call
    create-media
    when an
    mId
    is needed for
    siviAssets[]
    .
  • The
    create-media
    API docs: https://developer.sivi.ai/docs/sivi-api/core-api/media/create-media
  • 该Skill是供其他Skill使用的工具——通常不直接由用户调用。
  • 品牌关联是可选的——API不需要
    bId
    。若已匹配到品牌,则传入其ID;若未匹配到品牌,则完全省略
    bId
  • 文件不会保存到
    brands/
    文件夹。如需支持文件夹存储的品牌级资产管理,请使用
    brand-assets
    (即将推出)。
  • 使用生成API时,
    siviAssets
    +
    assets
    总计最多支持4个图片资产
  • 如需独立的AI图片增强(而非生成)功能,请使用
    enhance-media
    并指定
    model: "nano-banana:1k"
  • 直接图片URL(源2)可直接用于
    assets.images[]
    ,无需调用该Skill。仅当需要
    mId
    用于
    siviAssets[]
    时才调用
    create-media
  • create-media
    API文档:https://developer.sivi.ai/docs/sivi-api/core-api/media/create-media