HiAPI
Blog

Tutoriel

Comment appeler seedream-4.5/image-to-image via l'API HiAPI : curl et Python

Une requête exécutable démontrée de bout en bout, couvrant l'entrée multi-images de référence, l'interrogation asynchrone et le traitement de la sortie

Oct 31, 2026
深墨绿底上,两组宽扁丝带以平纹编织方式交错成开放式网格,穿插克制的黄铜色与深松绿色条带,为教程文章封面图。

Ce que fait ce point de terminaison et quand le choisir

seedream-4.5/image-to-image transforme une ou plusieurs images de référence, combinées à une invite textuelle, en une nouvelle image. Il fait partie de la série image-à-image sur HiAPI : vous fournissez un contexte visuel et le modèle le réinterprète selon vos instructions. C'est précisément ce qui le distingue du texte-à-image — dans ce dernier, l'invite est la seule entrée et le modèle crée tout de toutes pièces.

Choisissez l'image-à-image lorsque vous souhaitez que le résultat visuel reste ancré dans une matière existante : restyler un actif existant, modifier l'éclairage ou l'arrière-plan tout en préservant la forme et la disposition d'un produit, itérer sur une composition sans perdre le sujet, ou garantir la cohérence d'apparence d'une série de résultats en réutilisant les mêmes images de référence.

Choisissez le texte-à-image lorsque vous n'avez aucune matière de référence et souhaitez simplement décrire une scène avec des mots.

Ce point de terminaison est asynchrone. Vous soumettez une tâche, recevez un identifiant, puis interrogez jusqu'à ce que la tâche atteigne un état terminal, avant de récupérer la sortie. Planifiez ce cycle de vie dès le départ ; le reste de ce guide construit une requête exécutable, puis la renforce.

Construire une requête curl exécutable

Chaque requête envoyée à ce point de terminaison partage la même structure de base. Traitez-la champ par champ.

Point de terminaison et méthode

La requête est envoyée en POST vers la route image-à-image sous l'URL de base de HiAPI, avec l'identifiant de modèle seedream-4.5/image-to-image. Le nom du modèle est une valeur dans la charge utile de la requête, et non une partie du chemin.

Authentification

Transmettez votre clé API HiAPI en tant que jeton bearer dans l'en-tête Authorization. Conservez-la dans une variable d'environnement afin qu'elle n'apparaisse ni dans l'historique du shell ni dans la gestion de code source :

export HIAPI_API_KEY="your-key-here"

Charge utile minimale de la requête

curl -sS -X POST "$HIAPI_BASE_URL/v1/images/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "seedream-4.5/image-to-image",
    "prompt": "restyle this photo as a warm studio portrait",
    "image": "https://example.com/reference.jpg"
  }'

Explication champ par champ :

  • model — l'identifiant littéral seedream-4.5/image-to-image.
  • prompt — ce qu'il faut faire de l'image de référence. Décrivez la transformation, pas seulement le sujet.
  • image — l'entrée de référence. Pour une seule image, il s'agit d'une valeur ; la section suivante aborde les images de référence multiples.

Lire la première réponse

L'appel renvoie une tâche acceptée, et non l'image terminée. Capturez l'identifiant de tâche depuis le corps de la réponse et conservez-le ; vous en aurez besoin lors de l'interrogation. Ne considérez pas un statut de type 200 comme signifiant « image prête ».

Exécutez cette requête telle quelle une fois avant d'écrire du code client. Une charge utile qui échoue avec curl échouera en Python pour les mêmes raisons, et le débogage est plus rapide dans le shell.

Porter la requête en Python

La version Python conserve la charge utile à l'octet près, en ne remplaçant que la couche de transport et la gestion de l'authentification. Cette séparation est essentielle : si curl fonctionne, Python devrait fonctionner du premier coup.

import os
import requests

BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]

payload = {
    "model": "seedream-4.5/image-to-image",
    "prompt": "restyle this photo as a warm studio portrait",
    "image": "https://example.com/reference.jpg",
}

headers = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

resp = requests.post(
    f"{BASE_URL}/v1/images/generations",
    json=payload,
    headers=headers,
    timeout=30,
)
resp.raise_for_status()
job = resp.json()
print(job["id"])

Les notes suivantes vous feront gagner du temps plus tard :

  • Construisez la charge utile sous forme de dict Python et laissez json= gérer la sérialisation. Les chaînes JSON écrites à la main sont une source d'erreurs de guillemets.
  • Regroupez la construction de l'authentification dans une fonction utilitaire. Si le format de l'en-tête change, un seul endroit est concerné.
  • Passez toujours un timeout explicite à l'appel de soumission. Un blocage de socket à l'étape de soumission est indissociable d'un modèle lent.
  • Stockez immédiatement l'identifiant de tâche renvoyé, au lieu de le redériver depuis les journaux.

Le code d'interrogation des sections suivantes utilise les mêmes BASE_URL, API_KEY et headers, alors placez-les dans la portée du module ou dans une petite classe client.

Transmettre plusieurs images de référence

L'entrée multi-images de référence est l'endroit où les requêtes image-à-image échouent le plus souvent, généralement parce que le modèle reçoit trois images alors que l'invite n'en décrit qu'une seule.

Organiser les entrées de manière explicite

Placez les images de référence dans une liste sous le même champ image, dans l'ordre souhaité, et faites indiquer par l'invite le rôle de chaque position :

payload = {
    "model": "seedream-4.5/image-to-image",
    "prompt": (
        "Use the first reference for the subject's pose, "
        "the second for the background setting, "
        "and the third for color grading. Keep the subject sharp."
    ),
    "image": [
        "https://example.com/pose.jpg",
        "https://example.com/background.jpg",
        "https://example.com/grading.jpg",
    ],
}

Pourquoi l'annotation prévaut sur la position

L'ordre de la liste est une convention, mais c'est l'invite qui indique au modèle comment interpréter cet ordre. Un tableau vague avec une invite ambiguë force le modèle à deviner quelle référence prime lorsque les images entrent en conflit. Préciser le rôle de chaque image dans l'invite transforme des hypothèses implicites de position en instructions explicites.

Règles pratiques :

  • Gardez un petit nombre d'images de référence, chacune avec un objectif clair. Les références superflues diluent le résultat plutôt que de l'enrichir.
  • Indiquez la résolution des conflits : si l'image de référence de pose et celle d'arrière-plan ne concordent pas sur le cadrage, précisez laquelle prime.
  • Utilisez des URL stables et accessibles publiquement, ou un mécanisme de téléversement équivalent. Une image de référence qui renvoie un 404 en cours de tâche fait échouer toute la requête.

Si la sortie ignore une image de référence, la solution habituelle n'est pas de rappeler le modèle, mais d'indiquer le rôle de cette image dans une phrase d'invite plus claire.

Gérer le cycle de vie asynchrone d'une tâche

La génération d'images prend du temps, c'est pourquoi ce point de terminaison repose sur des tâches. Considérez le cycle de vie comme quatre étapes distinctes et écrivez le code de chacune.

1. Soumission

Envoyez le POST présenté plus haut. Avant toute autre chose, persistez l'identifiant de tâche renvoyé dans la réponse.

2. Interrogation

Avec les mêmes en-têtes d'authentification, émettez une requête GET vers la route de la tâche jusqu'à ce que le statut atteigne un état terminal :

import time

def poll_job(job_id, interval=2.0, max_interval=30.0, deadline=300.0):
    start = time.monotonic()
    while True:
        r = requests.get(
            f"{BASE_URL}/v1/images/generations/{job_id}",
            headers=headers,
            timeout=30,
        )
        r.raise_for_status()
        data = r.json()
        status = data["status"]
        if status in ("succeeded", "failed"):
            return data
        if time.monotonic() - start > deadline:
            raise TimeoutError(f"job {job_id} not finished within {deadline}s")
        time.sleep(interval)
        interval = min(interval * 2, max_interval)

3. Détecter l'état terminal

Seuls deux résultats mettent fin à la boucle : succès ou échec. Tout le reste signifie qu'il faut continuer à attendre. Ne supposez jamais que la sortie est immédiate, et ne fixez jamais un nombre d'interrogations fixe sans véritable échéance temporelle.

4. Récupérer le résultat

Traité dans la section suivante.

Le backoff est important. Une boucle serrée de sleep(0.1) consomme votre budget de débit en vérifications de statut. Commencer à environ deux secondes et doubler jusqu'à un plafond maintient une faible latence sans submerger l'API.

Récupérer et stocker la sortie

Lorsque l'interrogation renvoie un statut de succès, la réponse porte une référence de sortie. Ce qui est renvoyé est un pointeur vers l'image générée, pas nécessairement les octets eux-mêmes.

Télécharger délibérément

data = poll_job(job_id)
url = data["output"][0]["url"] if isinstance(data["output"], list) else data["output"]

with requests.get(url, stream=True, timeout=60) as r:
    r.raise_for_status()
    with open(f"{job_id}.png", "wb") as f:
        for chunk in r.iter_content(chunk_size=8192):
            f.write(chunk)

Adaptez le chemin selon la structure de réponse réelle observée lors de votre première tâche réelle, plutôt que de supposer un schéma fixe.

Règles de stockage pour éviter le retraitement

  • Nommez les fichiers d'après l'identifiant de tâche. Il est unique, traçable et associe l'artefact à la requête qui l'a produit.
  • Téléchargez rapidement. Les liens de sortie peuvent être soumis à une limite de temps, alors persistez immédiatement après une interrogation réussie, plutôt que de mettre l'URL en cache pour plus tard.
  • Traitez les gros fichiers en flux, plutôt que de les lire entièrement en mémoire.
  • Enregistrez l'invite et la liste des images de référence avec le fichier. Lorsque la sortie est inattendue, les entrées constituent le journal de débogage.

Si vous avez besoin de nouvelles tentatives déterministes, stocker l'identifiant de tâche signifie que vous pouvez réinterroger ou retélécharger sans payer une seconde génération.

Erreurs, nouvelles tentatives et maîtrise des coûts

La plupart des échecs appartiennent à l'une de trois catégories, chacune nécessitant un traitement différent.

Erreurs client

Charge utile mal formée, model manquant, URL d'image de référence inaccessible ou clé erronée. Ces erreurs sont renvoyées immédiatement et le seront encore en cas de nouvelle tentative. Corrigez la requête ; ne bouclez pas sur des tentatives. Vérifiez que les URL des images de référence se résolvent avant la soumission.

Erreurs serveur et réseau temporaires

Délais d'attente, réinitialisations de connexion, réponses de limitation de débit. Elles méritent une nouvelle tentative avec backoff exponentiel et jitter, plafonnée à un petit nombre maximal de tentatives. Réessayez l'étape précise — soumission, interrogation ou téléchargement — plutôt que l'ensemble du pipeline. Resoumettre après une soumission réussie est précisément la façon de payer deux fois par accident.

Échecs au niveau de la tâche

La tâche atteint un état terminal d'échec. Réessayer avec le même identifiant de tâche n'aidera pas. Décidez explicitement de resoumettre ou non avec une invite ou un jeu d'images de référence modifié, et enregistrez la cause de l'échec afin de distinguer une erreur d'entrée d'une défaillance temporaire.

def with_retries(fn, attempts=4, base=1.0):
    for i in range(attempts):
        try:
            return fn()
        except requests.RequestException:
            if i == attempts - 1:
                raise
            time.sleep(base * (2 ** i))

Maîtrise des coûts

La facturation est liée à la génération, donc l'erreur coûteuse est la resoumission répétée. Imposez une échéance temporelle réelle à l'interrogation, rendez la soumission idempotente en stockant l'identifiant de tâche avant d'interroger, et limitez la concurrence afin qu'une tempête de nouvelles tentatives ne se transforme pas en dizaines de tâches facturées. Consignez chaque soumission et son identifiant de tâche ; ce journal est votre piste d'audit des dépenses.

En intégrant ces trois préoccupations une fois pour toutes dans le client, le même code gère les requêtes à une ou plusieurs images de référence sans modification.

Articles similaires

Voir tous les articles →

Commencez avec des artefacts IA persistants

Obtenez votre clé API et appelez des modèles d'image, de vidéo et d'audio via une API unique basée sur les tasks.

Lire la documentation de l'API