HiAPI
Blog

Tutoriale

Come chiamare seedream-4.5/image-to-image tramite l'API HiAPI: curl e Python

Una richiesta eseguibile end-to-end, che copre input con più immagini di riferimento, polling asincrono e gestione dell'output

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

Cosa fa questo endpoint e quando sceglierlo

seedream-4.5/image-to-image trasforma una o più immagini di riferimento più un prompt testuale in una nuova immagine. Fa parte della famiglia image-to-image di HiAPI: fornisci un contesto visivo e il modello lo reinterpreta in base alle tue istruzioni. È qui la differenza chiave rispetto al text-to-image: in text-to-image il prompt è l'unico input e il modello crea tutto dal nulla.

Scegli image-to-image quando vuoi che il risultato visivo resti ancorato a materiale esistente: ridisegnare lo stile di un asset esistente, cambiare illuminazione o sfondo mantenendo forma e layout del prodotto, iterare la composizione senza perdere il soggetto, oppure garantire un aspetto coerente a un lotto di risultati riutilizzando le stesse immagini di riferimento.

Scegli text-to-image quando non hai materiale di riferimento e vuoi solo descrivere una scena a parole.

Questo endpoint è asincrono. Invii un job, ricevi un identificatore, poi esegui il polling finché il job raggiunge uno stato terminale e recuperi l'output. Pianifica questo ciclo di vita fin dall'inizio; il resto di questa guida costruisce una richiesta eseguibile e poi la rende più solida.

Costruire una richiesta curl eseguibile

Ogni richiesta a questo endpoint ha la stessa struttura di base. Affronta i campi uno per uno.

Endpoint e metodo

La richiesta viene inviata con POST alla rotta image-to-image sotto l'URL di base di HiAPI, usando l'identificatore del modello seedream-4.5/image-to-image. Il nome del modello è un valore nel payload della richiesta, non fa parte del percorso.

Autenticazione

Passa la tua chiave API HiAPI come bearer token nell'intestazione Authorization. Tienila in una variabile d'ambiente, così non finisce nella cronologia della shell o nel controllo del codice sorgente:

export HIAPI_API_KEY="your-key-here"

Payload minimo della richiesta

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"
  }'

Campo per campo:

  • model — l'identificatore letterale seedream-4.5/image-to-image.
  • prompt — cosa fare con l'immagine di riferimento. Descrivi la trasformazione, non solo il soggetto.
  • image — l'input di riferimento. Per una singola immagine è un singolo valore; la sezione successiva tratta più immagini di riferimento.

Leggere la prima risposta

La chiamata restituisce un job accettato, non un'immagine finita. Cattura l'identificatore del job dal corpo della risposta e conservalo; ti servirà durante il polling. Non considerare uno stato di tipo 200 come "immagine pronta".

Prima di scrivere qualsiasi codice client, esegui questa richiesta una volta così com'è. Un payload che fallisce in curl fallirà in Python per gli stessi motivi, e il debug nella shell è più rapido.

Portare la richiesta in Python

La versione Python mantiene il payload byte per byte identico, sostituendo solo il livello di trasporto e la gestione dell'autenticazione. È proprio questa separazione il punto chiave: se curl funziona, Python dovrebbe funzionare al primo tentativo.

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"])

Queste accortezze ti risparmieranno tempo in seguito:

  • Costruisci il payload come dict Python e lascia che json= si occupi della serializzazione. Le stringhe JSON scritte a mano sono la fonte tipica di errori di virgolette.
  • Mantieni la costruzione dell'autenticazione in una funzione di supporto. Se il formato dell'intestazione cambia, modifichi un solo punto.
  • Passa sempre un timeout esplicito anche alla chiamata di invio. Un socket bloccato nella fase di invio è indistinguibile da un modello lento.
  • Salva subito l'identificatore del job restituito, invece di ricavarlo dai log.

Il codice di polling nelle sezioni successive usa gli stessi BASE_URL, API_KEY e headers, quindi tienili a livello di modulo o in una piccola classe client.

Passare più immagini di riferimento

L'input con più immagini di riferimento è il punto in cui le richieste image-to-image sbagliano più spesso, di solito perché il modello riceve tre immagini e il prompt ne descrive solo una.

Organizzare gli input in modo esplicito

Metti le immagini di riferimento come lista sotto lo stesso campo image, nell'ordine che desideri, e fai sì che il prompt indichi il ruolo di ciascuna posizione:

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",
    ],
}

Perché etichettare è meglio della posizione

L'ordine della lista è una convenzione, ma è il prompt a dire al modello come interpretare quell'ordine. Un array generico con un prompt vago costringe il modello a indovinare quale riferimento ha la precedenza quando le immagini sono in conflitto. Indicare nel prompt il ruolo di ciascuna immagine trasforma le ipotesi implicite sulla posizione in istruzioni esplicite.

Regole pratiche:

  • Mantieni il numero di riferimenti piccolo e assegna a ciascuno uno scopo chiaro. I riferimenti superflui diluiscono il risultato invece di arricchirlo.
  • Indica come risolvere i conflitti: se l'immagine di riferimento per la posa e quella per lo sfondo non concordano sull'inquadratura, specifica quale ha la precedenza.
  • Usa URL stabili e accessibili pubblicamente, o un meccanismo di upload equivalente. Un riferimento che restituisce 404 a metà job fa fallire l'intera richiesta.

Se l'output ignora un'immagine di riferimento, la soluzione di solito non è richiamare il modello, ma indicare il ruolo di quell'immagine con una frase più chiara nel prompt.

Gestire il ciclo di vita asincrono del job

La generazione di immagini richiede tempo, quindi questo endpoint si basa su job. Considera il ciclo di vita come quattro passaggi distinti e scrivi il codice per ciascuno.

1. Invio

Invia il POST mostrato sopra. Prima di fare qualsiasi altra cosa, rendi persistente l'identificatore del job dalla risposta.

2. Polling

Con le stesse intestazioni di autenticazione, invia una richiesta GET alla rotta del job finché lo stato non raggiunge uno stato terminale:

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. Rilevare lo stato terminale

Solo due esiti chiudono il ciclo: successo o fallimento. Qualsiasi altro caso significa continuare ad attendere. Non dare mai per scontato che l'output sia immediato, e non fissare mai un numero fisso di tentativi di polling senza un limite di tempo effettivo.

4. Recuperare il risultato

Trattato nella sezione successiva.

Il backoff è importante. Un ciclo serrato con sleep(0.1) consuma il tuo budget di frequenza in controlli di stato. Partire da circa due secondi e raddoppiare fino a un limite mantiene bassa la latenza senza sommergere l'API.

Recuperare e salvare l'output

Quando il polling restituisce uno stato di successo, la risposta contiene un riferimento all'output. Viene restituito un puntatore all'immagine generata, non necessariamente i byte stessi.

Scaricare in modo intenzionale

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)

Adatta il percorso in base alla struttura di risposta effettiva osservata dal tuo primo job reale, invece di presumere uno schema fisso.

Regole di archiviazione per evitare rifacimenti

  • Assegna al file il nome basato sull'identificatore del job. È univoco, tracciabile e collega l'artefatto alla richiesta che l'ha prodotto.
  • Scarica tempestivamente. I link all'output possono avere una scadenza, quindi rendi persistente subito dopo un polling riuscito, invece di mettere da parte l'URL per dopo.
  • Gestisci i file di grandi dimensioni in streaming, invece di caricarli interamente in memoria.
  • Registra il prompt e la lista delle immagini di riferimento insieme al file. Quando l'output è inatteso, gli input sono la traccia di debug.

Se hai bisogno di retry deterministici, conservare l'identificatore del job significa che puoi ripetere il polling o riscaricare senza pagare per una seconda generazione.

Errori, retry e controllo dei costi

La maggior parte dei fallimenti rientra in tre categorie, ciascuna delle quali richiede una gestione diversa.

Errori del client

Payload malformato, model mancante, URL dell'immagine di riferimento non raggiungibile o chiave errata. Questi errori vengono restituiti immediatamente e verranno restituiti di nuovo anche in caso di retry. Correggi la richiesta; non ripetere in ciclo. Verifica che gli URL delle immagini di riferimento si risolvano prima dell'invio.

Errori temporanei del server e di rete

Timeout, reset di connessione, risposte di limite di frequenza. Questi meritano un retry con backoff esponenziale e jitter, con un limite basso di tentativi massimi. Ripeti il passaggio specifico — invio, polling o download — invece dell'intera pipeline. Reinviare dopo un invio riuscito è esattamente il modo in cui si paga due volte per errore.

Fallimenti a livello di job

Il job raggiunge uno stato terminale di fallimento. Ripetere lo stesso identificatore di job non aiuta. Decidi esplicitamente se reinviare con prompt o set di riferimenti modificati, e registra il motivo del fallimento per distinguere gli errori di input dai guasti temporanei.

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))

Controllo dei costi

La fatturazione è legata alla generazione, quindi l'errore più costoso è l'invio duplicato. Imponi un limite di tempo effettivo al polling, mantieni idempotente l'invio conservando l'identificatore del job prima del polling, e limita la concorrenza, così una tempesta di retry non si gonfia fino a decine di job fatturati. Registra ogni invio e il relativo identificatore di job; quel log è la tua traccia di audit delle spese.

Integrando questi tre aspetti una volta sola nel client, lo stesso codice gestirà richieste con uno o più riferimenti senza modifiche.

Inizia a creare con artefatti IA persistenti

Ottieni la tua chiave API e chiama modelli di immagini, video e audio tramite un'unica API basata sui task.

Leggi la documentazione API