星海智算开发文档
应用开发

从素材到生成结果

用 HTTP 和 Python 完成服务发现、素材准备、可选提示词优化、提交、恢复、查询和下载,区分联网调试与正式应用。

本章以 MiniMax H3 为完整样板。先确定调用身份,再依次完成:发现服务 → 准备素材 → 可选优化 → 提交一次 → 查询同一任务 → 下载结果。图像、音频和文档任务沿用相同的异步读取方式,但创建地址和请求字段不同,不能直接套用 H3 的参数。

1. 先选对入口

场景地址来源和身份创建请求输入素材Python 入口
个人 Account API Keyhttps://api.jusuanhub.com:10443/v1;个人创建的 API KeyPOST /media/generations同一凭据上传所得 assetIdvideo_generation(payload, idempotency_key=...)
应用联网调试jusp app dev --online 注入的地址和短期令牌;入口为 https://app-api.jusuanhub.com:10443/v1POST /media/generations同一调试身份上传所得 assetId同上;客户端自动读取注入配置
应用本地模拟jusp app dev --local 注入的回环地址和模拟身份同联网调试路径模拟素材;不产生真实模型结果同上;不计费,不能据此判断模型效果
已部署应用JUSP_APP_RUNTIME_BASE_URL 和平台托管凭据,加用户 RuntimeSessionPOST /media/jobs应用自己的 inputs[].readUrl,平台受控读取create_job(payload, idempotency_key=...)

正式地址已经包含平台要求的基础前缀,直接拼接本章的相对路径,不能手工再补 /v1。个人、调试、正式应用的素材身份不互通。不要把个人素材 ID 放进正式应用的 params,也不要把公开创建正文原样传给 create_job

公网下文的 API_BASEAPI_TOKEN 来自所选身份,均只在后端或本地环境保存。联网调用会按当前价格计费;服务发现、查询成功不是免费推理的承诺。预检不会锁定额度或算力;优化与生成是两个独立调用,不会自动互相触发。

2. 发现服务与规格

HTTP:GET {API_BASE}/services?serviceKind=video_generation,无请求体。公网响应是 {"object":"list","data":[...]};正式应用用 GET {JUSP_APP_RUNTIME_BASE_URL}/available-services。使用发现结果的公开模型别名 modelAlias(SDK 归一为 model),不使用部署名、实例 ID 或平台内部发布编号。

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/services?serviceKind=video_generation"

检查服务可用性、mediaSpecContractpromptOptimization。H3 当前规格为 768p、横向 1376×768 或纵向 768×1376、24 fps、5~15 整数秒,实际最高 362 帧。不要推算为固定 365 帧,不提交 fps、宽高、帧数、推理步数或设备参数。

3. 准备素材:POST /assets/input

仅用于个人 Key 或联网调试。查询参数 model 必填;请求为 multipart/form-data,每次上传一个文件。通用字段名为 file,也接受 imagevideoaudio。浏览器或 HTTP 库生成 multipart boundary,不手工设置不完整的 Content-Type。

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${API_TOKEN}" \
  -F 'file=@storyboard.png;type=image/png' \
  "${API_BASE}/assets/input?model=minimax-h3"

成功为 201,从 asset.assetId 取素材 ID,不是 id 或顶层 assetId。主要响应字段:

字段类型含义
asset.assetIdstring后续引用的受控素材标识
asset.kind / mimeType / byteSizestring / string / integer识别出的类别、MIME 和字节数
asset.statusstring可引用状态为 available
asset.createdAt / lastUploadedAt / expiresAtdate-time创建、最近上传、到期时间;按返回期限使用
asset.reusedboolean相同身份和模型范围内复用了已有素材
usageobject,可选本次操作的用量信息;不是素材对象

默认短期有效期为24小时;以 expiresAt 为准。已受理任务持有它需要的素材,不要求用户持续上传保活。输入素材不是永久资产库,过期后需重新准备。415 表示格式不支持;413 表示体积超限;422 表示素材事实或规格不符合要求;404/403 不允许换身份绕过校验。

H3 全能参考:图片 JPG/PNG/WEBP ≤30 MiB;视频 MP4 ≤50 MiB;音频 WAV/MP3/FLAC/M4A/AAC/OGG,或包含音轨的 MOV,≤15 MiB,每段2~15秒、最多3段且合计≤15秒。MOV 必须使用 audio 上传字段,保留原始字节和音频参考身份,不占视频名额;是否能解码仍取决于实际编码。首尾帧图片另有16 MiB限制。详细像素与预处理规则见H3 视频 API

4. 可选优化:POST /media/prompt-optimizations

Content-Type 为 application/json。必须发送 Idempotency-Key,值与 clientRequestId 完全相同。预计0~3分钟,调用超时按实时 capability 的 clientTimeoutSeconds 配置;不要把3分钟文案当作必然完成的 SLA。

字段类型 / 必填约束
clientRequestIdstring / 是8~128字符,只含字母、数字、点、下划线、冒号、连字符;一次业务优化持久保存一个值
modelstring / 是服务发现的公开模型别名
generationModestring / 是与随后生成一致的模式
originalPromptstring / 是H3 非空且≤7000 Unicode字符;不按 UTF-8 字节数计数
mediaSpecobject / 是H3 提交 resolutionTierorientationseconds,与生成规格一致
referenceInputsarray / 是无素材时 [];每项只包含 roleassetId不要附加 mediaType
audioReferencePolicystring / 有音频时必填固定 preserve_without_understanding
stylestring / 否1~200字符的创作风格偏好

优化器中的首帧、尾帧使用 first_framelast_frame;全能参考使用 reference_imagereference_videoreference_audio。下例中的 ID 先由上一步上传取得:

{
  "clientRequestId": "storyboard-opt-001",
  "model": "minimax-h3",
  "generationMode": "universal_reference_video",
  "originalPrompt": "参考分镜图,按从左到右、从上到下的顺序表现雨夜站台递伞和共同离开。",
  "mediaSpec": {"resolutionTier": "768p", "orientation": "landscape", "seconds": 5},
  "referenceInputs": [{"role": "reference_image", "assetId": "input_storyboard"}]
}

成功为 201,响应 requestId 用于诊断,optimization.optimizedPrompt 是优化后的文本;optimization.statussucceeded,另有 optimizationId、模式、原文、创建/到期时间和可选 outcome/noticeCodes。确认后把完整优化文本放进生成的 prompt,不需要传 optimizationId 才能生成。

音频文件不送给 VLM,不做音频内容识别,只按原文保留音色参考等明确用途。输出以约6000字符为写作目标,最终严格≤7000;超限最多一次定向压缩,不能直接截断。失败保留原文,不自动生成视频,也不能静默删除某个参考素材。网络结果未知时仅用同一正文和同一幂等键恢复,不能新建键重复优化。完整错误说明见提示词优化 API

5. 创建公网视频:POST /media/generations

Content-Type 为 application/json。发送稳定的 Idempotency-Key;缺省不构成安全重试合同。X-Client-Request-Id 可选,仅供关联,不能代替幂等键。请求参数在正文顶层,没有 params 包裹,也不需要 serviceKind

参数类型 / 必填范围、默认和条件
modelstring / 是minimax-h3,须当前身份可调用
generationModestring / 是t2vi2vfirst_last_frame_videouniversal_reference_video
promptstring / 是非空,最多7000 Unicode字符;可以是用户确认的优化文本
resolutionTierstring / 是768p
orientationstring / 是landscapeportrait
secondsinteger / 是5~15;请求时长与最终媒体时长需区分
seedinteger / 否0~2147483647;省略由平台决定,不在重试时重新随机
input_image_asset_idstring / 条件必填i2v首帧;首尾帧模式中的首帧
end_image_asset_idstring / 条件必填仅首尾帧模式的尾帧
referenceInputsarray / 条件必填仅全能参考;每项role/assetId必填,mediaType可选且须匹配素材类型;有序且不能重复素材
模式素材组合
t2v不提供素材
i2v恰好一张首帧,不提供尾帧或全能参考数组
first_last_frame_video恰好首帧和尾帧,不提供全能参考数组
universal_reference_video无视频最多9图;有1个视频时最多8图;最多3音频,合计≤12项;至少1个图片或视频,不能只有音频

全能参考的 rolemediaType 必须成对:reference_image/imagereference_video/videoreference_audio/audio。同类素材按数组顺序编号;不要把优化请求的“只有role/assetId”结构和生成结构混为一谈。建议把连续分镜整理成一张六/九宫格故事板并明确阅读顺序,有助于表达镜头连贯性;它不是强制规则,也不承诺所有输入都更快。

{
  "model": "minimax-h3",
  "generationMode": "universal_reference_video",
  "prompt": "参考分镜图,按从左到右、从上到下的顺序表现雨夜站台递伞和共同离开。",
  "resolutionTier": "768p", "orientation": "landscape", "seconds": 5,
  "seed": 1234,
  "referenceInputs": [{"role": "reference_image", "assetId": "input_storyboard", "mediaType": "image"}]
}

首次受理为 202,表示排队/执行中,不表示已经生成成功。典型响应(省略可选元数据):

{"id":"job_example","jobId":"job_example","jobUrl":"https://api.example.test/v1/jobs/job_example?model=minimax-h3","object":"media.generation.job","status":"queued","requestId":"req_example","serviceKind":"video_generation","model":"minimax-h3","stage":"queued","retryAfterSeconds":3,"refreshAfterSeconds":3}

应原子保存幂等键、规范请求、jobIdrequestId。同键同正文恢复同一任务;同键改变正文会冲突。终态重放可能返回200或对应失败状态,不应强制只接收202。创建超时且还没取得jobId时,在同一身份下重放同键同正文,而不是查询一个未取得的jobId或换键再创建。H3不提供用户取消 API,关闭页面不会撤销已经受理的任务。

6. 查询和下载

GET /jobs/{jobId}?model={model}

创建响应同时以正文 jobUrl 和响应头 Location 返回可直接调用的绝对查询地址,并用 Retry-After 给出建议秒数。使用创建任务的同一 API Key 原样请求 jobUrl;兼容旧响应时才按 jobId + model 回退拼接。轮询的网络/HTTP成功不是任务成功,应读取正文 status。支持处理中 queued/running,终态 succeeded/failed;通用 Job 还可能有历史取消状态,不能据此推导 H3 可取消。客户端必须设置总等待上限,不能无界轮询或超时后自动重提生成请求。

字段类型含义
jobId / requestIdstring任务标识和服务端调用标识
jobUrlstring绝对鉴权查询地址;包含模型范围,原样使用,不用 publicationId 拼接
status / stage / stageMessagestring生命周期、展示阶段和说明;不用展示文案驱动业务分支
refreshAfterSeconds / retryAfterSecondsnumber,可选推荐刷新间隔,尊重服务端返回并做有界退避
elapsedSeconds / queuedSeconds / runningSecondsnumber,可选墙钟总时长、排队时长、执行时长;不是计费生成秒数
effectiveMediaSpecobject,可选平台解析后的实际规格;与请求规格区分
outputsarray,可选成功后的权威结果集合,含 assetId/mimeType/byteSize/contentUrl
assetsarray,可选兼容结果字段;尚未产出时可能省略,不一定是空数组
errorCode / errorMessage / statusCode / retryablestring / string / integer / boolean,可选任务失败详情,保留 requestId 用于诊断;失败查询本身仍返回 HTTP 200

succeeded 后从 outputs[] 取得结果,并优先以同一 Key 原样请求绝对 contentUrl。兼容旧响应时才读取 assets[].assetId 并按公开合同拼接。不要把结果 assetId 写成 jobId,不要猜对象存储 URL,也不要把 contentUrl 当成匿名永久外链。

GET /assets/{assetId}/content?model={model}

返回二进制正文,不能用 .json() 解析。保留 Authorization;用响应 Content-Type、实际长度及可用的摘要验证文件。HEAD 同路径只读取响应头,不下载正文。下载失败可重试同一读操作,不重新创建视频。

curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/jobs/${JOB_ID}?model=minimax-h3"
curl --fail-with-body --silent --show-error \
  -H "Authorization: Bearer ${API_TOKEN}" \
  "${API_BASE}/assets/${ASSET_ID}/content?model=minimax-h3" --output result.mp4

7. Python:联网调试完整示例

先按应用开发者工作台实时安装命令安装SDK,再通过 jusp app dev --online -- python demo.py 运行。下例使用已发行公开方法,不把返回字典误当作DTO。示例创建一次真实计费任务;生产应用应将业务键、请求和已受理标识持久保存后再继续轮询。

import time
from pathlib import Path
from jusp_sdk.runtime import RuntimeHttpClient

client = RuntimeHttpClient()  # 读取 jusp app dev 注入的地址和短期身份
model = "minimax-h3"
business_key = "project-42-shot-7-v1"  # 同一业务重试保留;新任务才更换
use_optimization = False  # 可选且单独计费;需要时设True
try:
    services = client.services(serviceKind="video_generation")
    uploaded = client.upload_input_asset(
        model=model, filename="storyboard.png",
        content=Path("storyboard.png").read_bytes(), mime_type="image/png",
    )
    asset_id = uploaded["assetId"]  # SDK 已提升 asset 信封
    payload = {
        "model": model, "generationMode": "universal_reference_video",
        "prompt": "按故事板从左到右、从上到下表现雨夜递伞与共同离开。",
        "resolutionTier": "768p", "orientation": "landscape", "seconds": 5,
        "referenceInputs": [{"role": "reference_image", "assetId": asset_id, "mediaType": "image"}],
    }
    if use_optimization:
        optimized = client.optimize_prompt({
            "clientRequestId": business_key + "-opt", "model": model,
            "generationMode": payload["generationMode"], "originalPrompt": payload["prompt"],
            "mediaSpec": {key: payload[key] for key in ("resolutionTier", "orientation", "seconds")},
            "referenceInputs": [{"role": "reference_image", "assetId": asset_id}],
        }, timeout=630)  # 应用中使用所选服务capability的clientTimeoutSeconds
        payload["prompt"] = optimized["optimization"]["optimizedPrompt"]
    accepted = client.video_generation(payload, idempotency_key=business_key)
    job_id = accepted["jobId"]  # 此处是字典,不是 RuntimeJob
    deadline = time.monotonic() + 900  # 调用方等待预算,不是服务完成时限
    while time.monotonic() < deadline:
        job = client.get_job(job_id, model=model)  # 此处才是 RuntimeJob
        if job.status == "succeeded":
            if not job.results:
                raise RuntimeError("任务成功但结果未就绪,请稍后查询同一任务")
            result = client.download_result_with_metadata(job_id, job.results[0].result_id, model=model)
            Path("result.mp4").write_bytes(result.content)
            print(result.content_type, result.byte_size, result.sha256)
            break
        if job.status in {"failed", "canceled"}:
            raise RuntimeError(f"任务终止:{job.job_id} / {job.status}")
        time.sleep(5)
    else:
        raise TimeoutError(f"停止本地等待,不取消任务;以后继续查询 {job_id}")
finally:
    client.close()

RuntimeDownload 的字段是 content: bytescontent_type: strbyte_size: intsha256: str。摘要和字节数由SDK对实际正文计算;没有 content_lengthetag 属性。head_asset() 是另一种返回字典的操作,不要混用其字段。

8. 正式应用:POST /media/jobs

使用 RuntimeHttpClient(session=session),其中session来自用户从应用中心打开后兑换的 RuntimeSession。SDK读取平台托管凭据。不要把用户主体ID当作session,也不要在浏览器里保存平台凭据。

正文与公网创建不同:

字段类型 / 必填说明
clientTaskIdstring / 是应用业务任务ID,必须非空;参与幂等范围
modelstring / 建议显式提供/available-services 返回的公开别名;不依赖“某类别的第一个服务”
serviceKindstring / 建议显式提供本例 video_generation;另支持image_generation/audio_generation/document_parse
paramsobject / 按模型必填业务内容,例如 prompt;不能将prompt放在顶层
mediaSpecobject / 按模型必填generationMode/resolutionTier/orientation/seconds;不与params重复同一字段
inputsarray / 有素材时必填每项包含role和应用可授权读取的readUrl;不提交个人素材ID
resourceAttributionobject / 否已在应用声明中允许的resourceType/resourceId,用于业务资源用量归属
idempotencyKeystring / 否可用正文幂等键;建议统一发送header,二者不要填不同值

inputs[]readUrl 必填,另有 role/mimeType/byteSize/sha256/expiresAt。URL必须由应用自己的存储签发,满足平台读取策略;不能指向任意用户内网地址或本机文件。期限需覆盖平台读取过程,摘要/字节数与内容一致;平台校验后暂存。H3角色为首帧 first_frame、尾帧 last_frame,或全能参考的三种 reference_*;数量及音频时长约束与公网相同。

client = RuntimeHttpClient(session=session)
job = client.create_job({
    "model": "minimax-h3", "serviceKind": "video_generation",
    "clientTaskId": "project-42-shot-7-v1",
    "params": {"prompt": "清晨湖面薄雾缓慢流动,固定镜头,自然环境声。"},
    "mediaSpec": {"generationMode": "t2v", "resolutionTier": "768p", "orientation": "landscape", "seconds": 5},
}, idempotency_key="project-42-shot-7-v1")
# 将以下三个值原子持久化;recovery_ref 是秘密,只在应用后端保存。
job_id, task_id, recovery_ref = job.job_id, job.client_task_id, job.recovery_ref

首次受理202,HTTP正文直接包含 platformJobId/clientTaskId/status/stage/jobRecoveryRefresults尚无结果时可能省略。SDK转换为 RuntimeJobresults为tuple,恢复引用保存在 recovery_ref。同键终态重放可返回200或相应失败码。正式响应不能套用公网的 assets[] 解析。

正常查询 GET /media/jobs/{jobId} 使用session;服务重启后的受控恢复使用该任务的 X-Jusp-Job-Recovery-Ref,不同时发送session头。SDK的 get_job(job_id, recovery_ref=...)download_result_with_metadata(job_id, result_id, recovery_ref=...) 自动选择恢复头。恢复引用只允许访问绑定的已有任务,不能用于新建任务、其它任务或代替用户登录。

正式下载是 GET /media/jobs/{jobId}/results/{resultId}/downloadRuntimeResult 包含 result_id/download_url,以及可选 mime_type/byte_size/sha256。原始HTTP结果对应 resultId/resultDownloadUrl/mimeType/byteSize/sha256。结果下载URL不代表匿名永久外链。

正式优化仍调用 /media/prompt-optimizations,但 referenceInputs 每项必须含应用素材的role/readUrl、正数byteSize及真实内容的小写64位sha256;可附mimeType/expiresAt。不能复制公网role/assetId示例,也不接受mediaType。优化成功只提取文本,再构造上述create_job正文;优化不会替应用创建任务或持久保存最终业务资产。

9. 错误与恢复决策

症状正确处理不应做的事
创建网络超时,结果未知原身份、原clientTaskId、原正文、原幂等键重放;有jobId后查询新建幂等键,再次扣费创建
job_recovery_unavailable任务可能已受理;同键同正文重放取得恢复引用当作未创建而新建任务
400/422字段或素材错误修正字段/素材后作为新的业务意图提交截断prompt、静默删除素材或无限重试
401/403身份错误刷新正确身份或让用户重新打开;已有任务用合法恢复引用混用个人Key、调试token与应用凭据
402余额不足告知用户充值/调整预算换身份继续重试
409幂等冲突/服务不可用查看错误code;冲突核对原正文,服务不可用重新发现对所有409采取同一重试动作
429或503暂不可用尊重Retry-After,有界退避;写操作保留幂等键无上限紧循环
Job正文status=failed保存jobId/requestId和错误,停止轮询;用户决定新尝试把HTTP读取成功当作生成成功
下载断线或校验失败重新读取原结果,校验完成后才保存为业务资产重新运行模型

更多恢复入口包括事件流、批量查询、导出和资源用量聚合,见应用 HTTP API参考。SDK全部方法、同步/异步差异见Python API参考

本页目录