教程
如何在 HiAPI 上调用 gpt-image-2/image-to-image:curl、Python 与一次可用的请求
面向开发者的图生图请求实操:提交参考图、轮询任务并下载结果
image-to-image 端点期望什么
HiAPI 上的 gpt-image-2/image-to-image 模型是一个异步图像生成端点。与阻塞等待图像返回的同步调用不同,这个端点会接收你的请求、将其排队,并返回一个任务句柄。随后你需要持续跟踪该任务,直到它完成。
在请求层面,你需要提供三样东西:
- 模型 id:
gpt-image-2/image-to-image。 - 一张参考图:模型应基于其进行转换或构建的源图像。这是图生图模式的决定性输入——没有它,你做的就是文生图。
- 提示词与选项:一段描述期望输出的文本指令,以及模型文档中列出的可选字段(例如尺寸或输出格式)。只发送模型文档列出的参数。
响应并不是图像本身,而是一个包含标识符和状态的任务对象。把这个状态当作唯一事实来源:pending 或 processing 表示继续等待,succeeded 表示结果已就绪,failed 表示出了问题,应当停止轮询。
从一开始就围绕这个生命周期设计你的客户端。本教程中的其他一切——curl、Python、轮询、下载——都建立在“提交与完成是两个独立时刻”这一事实之上。
准备你的 HiAPI 密钥与参考图
在写任何代码之前,先准备好两样东西。
一个 API 密钥。在 HiAPI 控制台创建一个,并将其存入环境变量,而不是直接粘贴到脚本中:
export HIAPI_API_KEY="your-key-here"
把密钥当作机密对待。不要提交到版本库,也不要在请求日志中记录它。
一张参考图。选一个你希望转换的本地图像文件。保持合理的尺寸——过大、高分辨率的源图是请求缓慢或被拒绝最常见的原因。把 PNG 或 JPEG 放在脚本所在目录下,第一次运行最省事:
ls -lh ./reference.png
记下文件路径;你将在 curl 和 Python 示例中都引用它。如果你打算从服务器调用该模型,请确认进程对该路径有读权限,并且文件是有效图像(不是扩展名错误的改名文件)。
密钥已导出、图像已在磁盘上,第一次请求所需的一切就齐了。
一个可用的 curl 请求
验证凭据和请求体结构最快的方式,是执行一次 curl 调用。它会提交任务并打印任务对象:
curl -sS https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2/image-to-image",
"input": {
"image": "https://example.com/reference.png",
"prompt": "Turn this photo into a watercolor illustration"
}
}'
关于这个示例有两点说明。第一,参考图通过 input.image 字段以 URL 形式传入;如果端点文档针对你的场景支持内联 base64 或 multipart 上传,请使用那种形式,但保持字段名一致。第二,提示词就是你描述转换的地方——要具体说明风格、构图以及哪些内容应保持不变。
运行它并读取响应。提交成功会返回一个任务 id 和一个非终态状态。如果得到 401,说明你的密钥缺失或错误。如果得到 422 或类似的验证错误,检查请求体——通常是模型 id 或 image 字段格式有误。
保存任务 id。后续步骤都依赖它。
用 Python 做同样的事
Python 版本应与 curl 调用完全对应,显式保留认证与请求体构建,避免任何东西被封装层隐藏。使用 requests:
import os
import requests
API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1"
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": "gpt-image-2/image-to-image",
"input": {
"image": "https://example.com/reference.png",
"prompt": "Turn this photo into a watercolor illustration",
},
}
response = requests.post(f"{BASE_URL}/tasks", json=payload, headers=headers, timeout=30)
response.raise_for_status()
task = response.json()
print(task["id"], task["status"])
如果你的集成发送的是本地文件而非 URL,请按端点文档编码并附加图像,而不是自创字段。纪律与 curl 中相同:把请求体构建为普通字典、发送、在解析之前检查 HTTP 状态。
把提交逻辑封装到一个返回任务 id 的函数中。这样下一节的轮询代码就与任务如何创建无关了。
轮询异步任务直至完成
提交调用在图像仍生成时就已经返回。要拿到结果,就轮询任务状态端点,直到任务进入终态。一个健壮的循环具备三个特性:轮询之间的延迟、最大等待时间,以及对失败状态的明确处理。
import time
def wait_for_task(task_id, headers, base_url, timeout=300, interval=3):
deadline = time.time() + timeout
while time.time() < deadline:
r = requests.get(f"{base_url}/tasks/{task_id}", headers=headers, timeout=30)
r.raise_for_status()
task = r.json()
status = task["status"]
if status == "succeeded":
return task
if status == "failed":
raise RuntimeError(f"Task failed: {task}")
time.sleep(interval)
raise TimeoutError(f"Task {task_id} did not finish within {timeout}s")
几点实用的改进:
- 退避:以较短的间隔开始并逐渐增大,或者保持一个足够宽裕的固定间隔,以免频繁冲击 API。图像生成并非瞬时完成。
- 超时:始终为总等待时间设上限。否则一个卡住的任务会无限期挂起你的 worker。
- 终态:只把
succeeded和failed视为终态。任何其他状态都意味着继续轮询。如果 API 将来新增中间状态,你的循环仍能正确运行。
如果你要运行大量任务,请在后台 worker 或队列中轮询,而不是阻塞一个 Web 请求。生命周期相同,只是并发模型不同。
下载并验证结果
一旦任务报告成功,任务对象中会包含生成图像的位置——通常是输出字段中的一个 URL。下载它并写入磁盘:
task = wait_for_task(task_id, headers, BASE_URL)
image_url = task["output"]["image"]
img = requests.get(image_url, timeout=60)
img.raise_for_status()
with open("result.png", "wb") as f:
f.write(img.content)
如果签名 URL 有有效期,请在完成后立即下载,或直接持久化文件,而不是把 URL 存起来以后再用。如果你的端点返回 base64 而非 URL,请先解码再写入。
一次快速的完整性检查可以避免令人困惑的下游故障:
from PIL import Image
with Image.open("result.png") as im:
im.verify()
print("valid image, bytes:", os.path.getsize("result.png"))
如果文件不是可解码的图像,verify() 会抛出异常,这能捕获被截断的下载以及被保存成图像的错误页面。检查字节大小也能告诉你响应是否合理。至此循环完成:提交、轮询、下载、验证。
排查常见故障
大多数首次集成会以几种可预见的方式失败。按以下顺序诊断。
认证错误(401/403)。密钥缺失、格式错误或缺少权限。确认环境变量已在执行请求的 shell 或进程中导出,并且 Authorization 头使用了 API 期望的确切前缀。
参考图无效或过大(400/422)。图像 URL 可能从 API 一侧不可达,文件可能损坏,或者尺寸与文件大小超出限制。先用一张小巧、确认可用的 PNG 测试。如果传 URL,确保它可公开获取;如果传内联数据,确认编码正确。
轮询超时。如果任务持续触达你的超时,请增大上限、延长间隔,或检查任务是否实际已失败,只是你没有把原因展示出来。在抛出异常前记录最后一个任务对象,以便查看状态。
速率限制(429)。进行退避并带抖动重试,而不是立即重试。如果你大量并行提交,请限制并发以免突发流量触发限制,并通过记录任务 id 让重试具备幂等性。
对于任何错误类别,记录 HTTP 状态、截断的响应体和任务 id——这三样通常就足以判断问题出在认证、请求体还是时序。先用 curl 示例隔离问题是在你的代码还是输入,然后把修复移植回 Python。