Tutoriale
Come chiamare muse-image-1.0/image-to-image su HiAPI API: curl, Python e un esempio di richiesta funzionante
Cosa invia esattamente una richiesta image-to-image
Prima di scrivere la prima riga di codice, è utile capire cosa si aspetta una chiamata image-to-image. A differenza di una richiesta puramente text-to-image, in cui il prompt è l'unico input creativo, muse-image-1.0/image-to-image richiede un'immagine sorgente, più le istruzioni su come trasformarla.
Come minimo, il payload della tua richiesta include:
model: l'identificatoremuse-image-1.0/image-to-image.image: l'immagine sorgente che vuoi modificare. Di solito viene inviata come stringa codificata in base64 o come URL accessibile pubblicamente, a seconda del formato accettato dall'endpoint. Consulta lo schema di input del modello per confermare il nome esatto del campo e il formato.prompt: la descrizione testuale della modifica che vuoi apportare. Sii specifico su cosa cambiare e cosa mantenere.- Parametri opzionali come strength o guidance (se il modello li espone) ti permettono di controllare quanto l'output si discosta dall'immagine sorgente. L'insieme esatto dei parametri varia da modello a modello, quindi verifica sempre con il riferimento API invece di dare per scontato.
Il cambio di mentalità chiave è questo: non stai generando da zero, stai fornendo un punto di partenza e una direzione. Prompt vaghi come "make it better" producono risultati imprevedibili, perché il modello ha troppa libertà. Prompt come "replace the sky with a sunset and keep the buildings unchanged" danno al modello un vincolo chiaro.
Inoltre, verifica in anticipo i vincoli dell'immagine: formati supportati, dimensioni massime e dimensione del file. Se la tua immagine sorgente è un PNG da 12 MB, potrebbe essere necessario ridurla o convertirla in JPEG prima che la richiesta venga accettata. Gestire correttamente l'immagine prima dell'invio evita un round trip e un messaggio di errore confuso.
Una volta che hai l'immagine sorgente e un prompt di modifica chiaro, puoi effettuare la prima chiamata.
Inviare la prima richiesta con curl
Il modo più rapido per convalidare l'integrazione è inviare la richiesta direttamente dal terminale. Il percorso esatto dell'endpoint e lo schema del payload sono definiti nel riferimento API del modello; l'esempio seguente mostra la forma generale, così puoi adattarlo alla specifica corrente.
curl -X POST https://api.hiapi.ai/v1/images/edits \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "muse-image-1.0/image-to-image",
"image": "https://your-bucket.example.com/source.jpg",
"prompt": "replace the sky with a warm sunset, keep the buildings unchanged",
"strength": 0.6
}'
Sostituisci $HIAPI_API_KEY con una variabile d'ambiente, invece di incollare direttamente la chiave. Se il tuo strumento lo supporta, esporta la chiave nella sessione della shell, così non rimane nella cronologia dei comandi.
La risposta restituita contiene un identificatore del job. Poiché la modifica delle immagini è ad alto consumo computazionale, l'API di solito non restituisce l'immagine completata nella stessa risposta. Riceverai invece un ID job e uno stato. Salva quell'ID: lo userai nel passaggio successivo per interrogare il risultato.
Se la chiamata fallisce, leggi attentamente il corpo della risposta di errore. Un 400 di solito indica un payload malformato o un formato immagine non supportato. Un 401 indica una chiave mancante o errata. Un 429 indica che stai inviando richieste troppo velocemente. Risolvere questi problemi in fase di terminale è molto più facile che fare debug in un'applicazione completa.
La stessa richiesta implementata in Python
Dopo che la chiamata curl funziona, convertila in una piccola funzione Python. Usare requests mantiene l'esempio con poche dipendenze e facile da portare altrove.
import os
import requests
HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]
BASE_URL = "https://api.hiapi.ai/v1/images/edits"
def submit_edit(image_url: str, prompt: str, strength: float = 0.6) -> str:
payload = {
"model": "muse-image-1.0/image-to-image",
"image": image_url,
"prompt": prompt,
"strength": strength,
}
headers = {
"Authorization": f"Bearer {HIAPI_API_KEY}",
"Content-Type": "application/json",
}
response = requests.post(BASE_URL, json=payload, headers=headers, timeout=30)
response.raise_for_status()
data = response.json()
return data["id"]
Alcune note su questa funzione:
- Il parametro
timeoutimpedisce allo script di bloccarsi all'infinito in caso di stallo della rete. Per una chiamata di invio, 30 secondi sono ampi, ma puoi regolarli secondo necessità. raise_for_status()converte gli errori HTTP in eccezioni, così puoi intercettarli in un unico punto.- Restituire solo l'ID job mantiene la funzione focalizzata. La logica di polling dovrebbe stare in una funzione separata.
Se preferisci inviare l'immagine sorgente come byte invece che come URL, leggi il file, codificalo in base64 e inserisci il risultato nel campo image. Prima di cambiare, verifica il formato previsto nella documentazione, perché le due codifiche non sono sempre intercambiabili.
Questa funzione è volutamente minimale. In un servizio reale aggiungeresti logging, gestione dei retry e validazione dei tipi, ma il pattern centrale della richiesta è esattamente quello che vedi qui.
Gestire job asincroni e interrogare i risultati
La modifica delle immagini richiede tempo — di solito da pochi secondi a decine di secondi, a seconda del prompt e della risoluzione. L'API gestisce la cosa accettando la richiesta, restituendo un ID job e lavorando in background. Il tuo compito è controllare ripetutamente finché lo stato non raggiunge uno stato terminale.
Il polling sembra semplice, ma un ciclo ingenuo che scatta ogni 100 millisecondi spreca quota e può attivare limiti di velocità. Usa un intervallo ragionevole e un limite massimo di tentativi:
import time
import requests
def poll_job(job_id: str, interval: float = 2.0, max_attempts: int = 30) -> dict:
url = f"https://api.hiapi.ai/v1/jobs/{job_id}"
headers = {"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"}
for attempt in range(max_attempts):
response = requests.get(url, headers=headers, timeout=15)
response.raise_for_status()
data = response.json()
status = data["status"]
if status == "succeeded":
return data
if status == "failed":
raise RuntimeError(data.get("error", "job failed"))
time.sleep(interval)
raise TimeoutError(f"job {job_id} did not finish after {max_attempts} attempts")
Un intervallo di due secondi è un punto di partenza ragionevole. Se i tuoi job di solito si completano entro cinque secondi, puoi iniziare a interrogare più frequentemente e poi aumentare progressivamente l'attesa. Se di solito richiedono più tempo, riduci la frequenza del polling per evitare chiamate non necessarie.
Il limite di tentativi è la tua rete di sicurezza. Senza di esso, un job bloccato potrebbe causare un ciclo infinito. Quando raggiungi il limite, lancia un errore esplicito, così il chiamante sa che l'operazione è andata in timeout invece di bloccarsi silenziosamente.
In produzione, valuta di spostare il polling in un worker in background o in una coda, così il thread che gestisce la richiesta non viene bloccato in attesa del rendering dell'immagine.
Scaricare e memorizzare l'immagine di output
Quando il job raggiunge lo stato succeeded, la risposta contiene una o più immagini di output, di solito fornite come URL. Il tuo passo successivo è scaricare l'immagine e memorizzarla in modo persistente.
def download_image(result: dict, dest_path: str) -> None:
image_url = result["output"][0]["url"]
response = requests.get(image_url, timeout=30)
response.raise_for_status()
with open(dest_path, "wb") as f:
f.write(response.content)
Alcune considerazioni pratiche:
- Gli URL di output potrebbero essere temporanei. Trattali come risorse a breve durata, scaricali prontamente invece di memorizzare l'URL come unico riferimento.
- Scrivi i file in modalità binaria. Le risposte immagine non sono testo; aprire con
"w"corromperebbe i dati. - Prima di dichiarare il successo, verifica che il file non sia vuoto. Un download da zero byte di solito significa che l'URL è scaduto o che la richiesta è stata bloccata.
Per qualsiasi scenario oltre a uno script locale, trasmetti il download direttamente al tuo provider di storage invece di salvarlo prima su disco. La maggior parte degli SDK di object storage accetta oggetti simili a file o flussi di byte, quindi puoi passare response.raw o response.content alla chiamata di upload senza file intermedi.
Infine, registra l'ID job insieme all'oggetto memorizzato. Se in seguito l'output si danneggia, o se devi riprodurre una modifica specifica, l'ID job più gli input originali ti permettono di ricostruire cosa è stato inviato.
Punti di errore comuni e come risolverli
La maggior parte dei problemi di integrazione rientra in poche categorie. Ecco come diagnosticare ciascuna.
La richiesta viene rifiutata prima della creazione del job
Un errore 400 al momento dell'invio di solito indica un payload errato. Controlla che model sia esattamente muse-image-1.0/image-to-image, che image sia accessibile o codificato correttamente in base64, e che prompt sia una stringa non vuota. Se l'immagine è un URL, verifica che sia accessibile pubblicamente — un bucket privato o un URL firmato scaduto non supererà la validazione.
Il job fallisce durante l'elaborazione
Se il polling restituisce lo stato failed, leggi il messaggio di errore nel corpo della risposta. Le cause comuni includono immagini troppo grandi, prompt che violano la policy sui contenuti o valori dei parametri fuori dall'intervallo consentito. Riduci le dimensioni dell'immagine e semplifica il prompt per isolare la causa.
Timeout del polling
Se raggiungi il limite di tentativi senza uno stato terminale, il job potrebbe effettivamente aver bisogno di più tempo, oppure potrebbe essere bloccato. Per prima cosa, aumenta max_attempts e verifica il tempo di elaborazione tipico nel riferimento API. Se i job continuano ad andare in timeout, controlla se l'immagine sorgente è insolitamente grande. Non rimuovere semplicemente il limite di tentativi — i retry limitati prevengono cicli infiniti silenziosi.
Limiti di velocità
Una risposta 429 significa che stai inviando richieste troppo velocemente. Fai backoff, rispetta qualsiasi header retry-after e scagliona gli invii concorrenti. Se stai elaborando un batch di immagini, elaborale in sequenza o usa un limite di concorrenza più basso, invece di inviarle tutte in una volta.
Output mancante o troncato
Se il job ha successo ma il file scaricato è vuoto, l'URL di output potrebbe essere scaduto tra il polling e il download. Scarica immediatamente dopo il successo. Se l'immagine è danneggiata, verifica di aver scritto in modalità binaria e di non aver applicato alcuna codifica testuale al flusso.
Il debug è più facile quando registri il payload della richiesta, l'ID job e le transizioni di stato. Con queste tre informazioni, quasi ogni errore può essere ricondotto alla sua origine.
Con la forma della richiesta, i pattern curl e Python, la logica di polling e i passaggi di download, hai una pipeline funzionante per usare muse-image-1.0/image-to-image su HiAPI API. Inizia dall'esempio da terminale, spostalo in uno script e aggiungi retry e logging dopo che il percorso normale è stabile.
Articoli correlati
Vedi tutti gli articoli →-
Tutoriale
Come chiamare hailuo-2.3-fast/image-to-video tramite l'API HiAPI: pratica con curl e Python
La prima richiesta funzionante per il livello image-to-video veloce ed economico — dall'ID del modello al download di un MP4
-
Tutoriale
Come chiamare hailuo-2.3/text-to-video tramite l'API HiAPI: curl, Python ed esempi di richiesta eseguibili
-
Tutoriale
Utilizzare Kandinsky 6.0 Pro su HiAPI per la generazione di video da testo: tutorial con curl e Python
Dimostrazione pratica guidata dalla richiesta: costruire la chiamata da prompt a clip video, inviare la richiesta, monitorare lo stato di completamento e scaricare il risultato