spicyapiDocumentazione
Guide
Contenuto principale

Guida: generare e modificare immagini

Come si comportano gli endpoint immagine in quanto categoria — le due forme di richiesta, i tre modi di scrivere la dimensione in uscita, cosa aggiunge al conto un'immagine di riferimento, i pesi di adattamento e come leggere un rifiuto.

Questa guida parla della categoria degli endpoint immagine, non di un modello in particolare. Spiega ciò che resta uguale fra tutti e, cosa più utile, i punti in cui divergono al punto che un corpo di richiesta scritto per uno non sopravvive al successivo.

Qui non troverete una tabella dei parametri, ed è una scelta. I parametri hanno un'unica fonte: l'inputSchema che il catalogo in tempo reale restituisce per il modello esatto che state chiamando. Questa pagina spiega come leggere quello schema e ciò che non dice ad alta voce.

Se cercateLeggete piuttosto
La prima immagine, dalla chiave al file, dall'inizio alla fineAvvio rapido
Lo stesso percorso per il video, passo per passoTutorial: trasformare una foto in un video
Tutti i campi di un modello precisoIl suo inputSchema — vedi Catalogo dei modelli e schemi
Portare un volto o una testa da una foto a un'altraSostituzione del soggetto

MODEL_ID_FROM_CATALOG è un segnaposto

Tutti gli esempi qui sotto scrivono MODEL_ID_FROM_CATALOG dove va un identificativo di modello reale. Prendete quell'identificativo dal catalogo in tempo reale autenticato — GET /api/v1/models?modality=image — e pretendete sia enabled: true sia available: true prima di spendere qualcosa. Leggere il catalogo è gratuito. Inviare il segnaposto così com'è restituisce solo un errore «not found».

Due forme di richiesta

Il lavoro sulle immagini arriva come due attività, e il catalogo le separa:

  • Gli endpoint di generazione finiscono in /text-to-image. Mandate parole, ricevete un'immagine.
  • Gli endpoint di modifica finiscono in /edit. Mandate anche almeno un'immagine.

La modifica ha due grafie ammesse, e conviene conoscerle entrambe prima di filtrare il catalogo: gli endpoint vengono pubblicati con il suffisso edit, mentre l'array tasks li classifica come image-to-image. Il parametro di query task accetta l'una o l'altra. Vedi Catalogo dei modelli e schemi.

Entrambe le forme usano la stessa busta di creazione, lo stesso ciclo di vita dell'attività e lo stesso formato di risultato di qualunque altro modello multimediale. Ciò che cambia è input.

prompt è obbligatorio in entrambe

Viene naturale supporre che un endpoint di modifica se la cavi con la sola immagine e che lì prompt sia facoltativo. Sugli endpoint che accettano un prompt non è così: prompt è nell'elenco required della radice in entrambe le forme, e ometterlo viene rifiutato prima che venga addebitato alcunché.

Rifiutato da un endpoint di modifica
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "image_urls": ["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]
  }
}
prompt: is required

L'immagine dice da dove partite; il prompt dice cosa farne. Un endpoint di modifica che non ha ricevuto istruzioni non ha su cosa agire.

C'è un'eccezione, ed è un'intera famiglia più che un caso isolato: alcuni endpoint di modifica non accettano nessun prompt e prendono solo immagini, perché l'operazione è fissata dall'endpoint stesso. Appartengono al kit di sostituzione del soggetto e portano nel catalogo un campo toolkit che permette di riconoscerli; vedi Sostituzione del soggetto. Per loro prompt: is not a supported field è la risposta giusta, non un difetto.

Il campo dell'immagine in ingresso ha tre nomi

Quale ne usi un endpoint fa parte del suo schema e non si deduce dal tipo di attività:

CampoFormaChe cosa significa di solito
image_urlun riferimento a immagineC'è esattamente un'immagine soggetto, e l'endpoint non contempla una seconda
image_urlsarray di riferimenti a immagineLe immagini sono il materiale modificato o combinato, nell'ordine a cui l'istruzione si riferisce
reference_image_urlsarray di riferimenti a immagineLe immagini orientano una generazione che altrimenti sarebbe solo testuale: sono guida, non il soggetto

Quello che sorprende è reference_image_urls: compare su endpoint di generazione, quindi un endpoint /text-to-image può benissimo accettare immagini. Non date per scontato che «text-to-image» voglia dire «nessuna immagine nella richiesta».

Un riferimento a immagine è un URL HTTPS pubblico, un URI spicy:// confermato dopo un caricamento, oppure un piccolo URI data:image/…;base64,…. Per i caricamenti vedi Media; per i nomi canonici, il contratto Spicy Schema.

I limiti degli array variano molto più di quanto si creda

Gli array qui sopra portano minItems e maxItems, il server applica entrambi, e i numeri differiscono da endpoint a endpoint: oggi si va da un array che accetta esattamente un elemento ad array che ne accettano dieci. Un endpoint della stessa famiglia, con lo stesso nome di campo, può avere un tetto diverso.

image_urls: must contain at least 1 items
image_urls: must contain at most 3 items

Leggete minItems e maxItems dallo schema del modello che state chiamando. Una tabella dei parametri che mostra un array come un nudo array, senza la forma degli elementi e senza limite di lunghezza, è proprio la riga con cui un lettore non può fare nulla — ed è esattamente per questo che questo sito non ne pubblica.

La dimensione in uscita si scrive in tre modi

È la prima cosa contro cui si inciampa puntando un corpo di richiesta esistente verso un secondo endpoint.

Nel catalogo immagini convivono tre grafie, e un endpoint ne accetta esattamente una:

GrafiaCampiNote
Separataaspect_ratio e resolutionLa forma dell'inquadratura e il gradino di pixel si scelgono separatamente
PreimpostazionesizeUn valore di enumerazione fissa entrambi in una volta
Esplicitawidth e heightPixel interi, ciascuno con il proprio minimum e maximum

La grafia separata è la più diffusa sugli endpoint recenti e la più utile delle tre: aspect_ratio compone l'inquadratura prima della generazione invece di ritagliare dopo, così potete cambiare la forma senza toccare il budget di pixel.

Un campo della grafia sbagliata viene rifiutato, non ignorato

Ogni inputSchema di immagine imposta additionalProperties: false. Un campo che l'endpoint non elenca viene respinto: un corpo di richiesta ripreso da un altro endpoint fallisce del tutto invece di ripiegare in silenzio su un valore predefinito.

Fallisce in entrambe le direzioni, e il messaggio nomina il campo:

size: is not a supported field
aspect_ratio: is not a supported field

Né l'uno né l'altro viene addebitato. Il costo non è in denaro, è nell'assunto: non potete scrivere un unico oggetto input e puntarlo verso più endpoint immagine, e non esiste una grafia «di compatibilità» che vada bene per tutti.

Quale seleziona una fascia di prezzo

Su un endpoint a grafia separata i due campi hanno di norma ruoli diversi nel conto:

  • resolution di solito seleziona una fascia di prezzo. Compare nel preventivo come variant, e spostarlo sposta l'importo.
  • aspect_ratio di solito no. Reinquadrare non è ridimensionare.

Quel «di solito» nelle due frasi sta lavorando sul serio. La risposta leggibile da una macchina è sul campo stesso: x-ui.affects_price: true e, quando l'endpoint lo dichiara, l'appartenenza a x-pricing.variantFields. Controllate lo schema, non l'abitudine. Vedi Leggere i prezzi e il riferimento delle annotazioni nel contratto Spicy Schema.

Anche i valori ammessi sono per endpoint. Un rifiuto li elenca, il che è un modo gratuito per vederli:

aspect_ratio: must be one of: "1:1", "16:9", "9:16", "4:3", "3:4", "3:2", "2:3", "21:9"

Comodo mentre si esplora, ma non sostituisce la lettura di enum, perché lo schema porta anche il default che si applica quando il campo viene omesso.

Cosa aggiunge al conto un'immagine di riferimento

Questa è la voce silenziosa, e merita una sezione propria perché nella risposta nulla la indica.

L'aritmetica consueta di un modello multimediale è prezzo × quantità, dove la quantità è il valore di quantityField nel vostro input, oppure una unità quando quantityField è vuoto. Sugli endpoint immagine quantityField è quasi sempre num_outputs, e si comporta come ci si aspetta: chiedete quattro immagini e il preventivo segna quantity a 4. Questa dimensione è onesta: la si vede muoversi.

Alcuni endpoint aggiungono poi un addebito separato per ogni immagine in ingresso, e quello non lo si vede muoversi. Chiedete due preventivi per lo stesso endpoint, uno con un solo riferimento e uno con l'array pieno, e ottenete:

  • la stessa unit;
  • la stessa quantity;
  • la stessa variant;
  • un importo diverso.

Tre dei quattro campi con cui di solito si costruisce una stima restano esattamente dov'erano. Una stima calcolata come prezzo × quantità risulta più bassa del vero, e nulla nella risposta lo segnala.

La firma leggibile da una macchina

Un campo marcato x-ui.affects_price: true che non è il quantityField né compare in x-pricing.variantFields sposta l'importo senza selezionare una fascia. Per un endpoint così, leggete il pricing del catalogo come un pavimento e prendete l'importo da un preventivo.

È la stessa regola enunciata in Catalogo dei modelli e schemi, e la modifica di immagini è il punto in cui la incontrerete più spesso: l'array delle immagini in ingresso porta di frequente quella marcatura.

Quanto si aggiunga dipende dall'endpoint e non segue alcuna proporzione fissa. Oggi il catalogo contiene endpoint in cui riempire l'array costa diverse volte un riferimento singolo, ed endpoint in cui i riferimenti aggiuntivi non costano nulla — senza modo di distinguerli se non chiedendo. Quindi non stimate il prezzo dell'array per analogia con un endpoint vicino, e non ricavate un array pieno da un preventivo con un solo riferimento.

Preventivate il corpo che manderete davvero

Dare un prezzo a questa richiesta esatta prima di pagarla
curl -sS https://api.spicyapi.ai/api/v1/jobs/quote \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"MODEL_ID_FROM_CATALOG","input":{"prompt":"your instruction","image_urls":["spicy://f/fil_01k3m8x9q2z4v7n5p6r8s0t1w3"]}}'

Un preventivo esegue la stessa convalida della creazione dell'attività, non costa nulla, non avvia nulla e non mette nulla da parte. Restituisce estimatedCost, maxCharge, quantity e unit per quel corpo esatto. Se cambiate l'array, preventivate di nuovo. I dettagli, e come rimediare quando la risposta va persa, sono in Preventivi e compatibilità; dove collocare un preventivo in un'integrazione reale è in Integrazione in produzione.

Pesi di adattamento

Alcuni endpoint permettono di applicare pesi vostri, messi a punto da voi, sopra al modello di base. Il campo canonico è loras, oppure high_noise_loras e low_noise_loras quando un endpoint espone le sue fasi separatamente.

Un array di oggetti, con regole proprie all'interno

Questi campi portano x-ui.widget: "object-list": un array il cui items è uno schema di oggetto completo a sé stante. Ha un proprio required, un proprio additionalProperties: false, e tipo, intervallo e default propri per ciascun sottocampo. Un elemento tipico nomina un insieme di pesi e con quanta forza applicarlo.

Due elementi applicati insieme
{
  "model": "MODEL_ID_FROM_CATALOG",
  "input": {
    "prompt": "your scene",
    "loras": [
      { "path": "https://weights.example.com/style.safetensors", "scale": 1 },
      { "path": "https://weights.example.com/character.safetensors", "scale": 0.6 }
    ]
  }
}

La forma di un elemento non è fissa da un endpoint all'altro. Che cosa conti come file di pesi — un indirizzo diretto o una chiave di una libreria integrata — lo restringe il sottoschema items del singolo endpoint, così come i nomi dei sottocampi, quanti elementi ci stanno e quali sono obbligatori. Leggete items, non questo esempio. Il contratto Spicy Schema descrive il widget; Catalogo dei modelli e schemi mostra come trasformarlo in una tabella annidata.

Una regola interna a questi elementi si legge male con facilità: i valori predefiniti arrivano dentro gli elementi dell'array. Un sottocampo che sta in items.required e ha anche un default è di fatto facoltativo: se lo omettete, il valore predefinito viene inserito prima della convalida. Un sottocampo senza predefinito, se manca, viene rifiutato. Quindi items.required non significa «deve comparire nel JSON che invio».

Da dove vengono i pesi

path accetta una di tre cose:

  • Un link pubblico al file .safetensors o, dove l'endpoint lo consente, un id di repository Hugging Face come owner/repo. Il modello lo scarica al momento della generazione, perciò la prima chiamata con pesi nuovi è sensibilmente più lenta di quelle successive.
  • Un caricamento. Caricate il file come application/octet-stream con il flusso di caricamento diretto, fino a 2 GiB, e usate l'URI spicy://f/… restituito. Il commit verifica che si tratti di un file safetensors completo.
  • Un'importazione. Per un file che richiede l'accesso, come uno in un repository Hugging Face privato o con accesso limitato oppure un file Civitai protetto da una chiave API, inviate il link e un token di accesso a POST /api/v1/files/import. SpicyAPI scarica il file una sola volta e restituisce un URI spicy://f/…. Il token serve solo per quel download e non viene mai conservato.
Importare un LoRA da un repository Hugging Face privato
curl -sS https://api.spicyapi.ai/api/v1/files/import \
  -H "Authorization: Bearer $SPICY_API_KEY" \
  -H "Content-Type: application/json" \
  --data "$(jq -n --arg url "https://huggingface.co/OWNER/REPO/blob/main/your_lora.safetensors" --arg token "$HF_TOKEN" '{url: $url, token: $token}')"

url può essere anche l'id del repository (OWNER/REPO) o la pagina del repository, quando contiene esattamente un file .safetensors. La risposta ha la stessa forma di quella di un commit; mettete data.uri in loras[].path. I pesi caricati e importati vengono conservati per un giorno, come ogni altro caricamento, e non sono mai condivisi con altri account. Dopo, caricateli o importateli di nuovo.

L'invio verifica il link, non i pesi al suo interno

Prima che l'attività venga creata, ogni link ai pesi viene sondato. Un link che richiede l'accesso, restituisce 404 o apre una pagina web viene rifiutato con un 400 e non viene addebitato nulla; il messaggio indica l'elemento. Tutto il resto passa, quindi un host che va giù più tardi, o pesi che non si riescono a caricare, vi arrivano comunque come attività fallita.

I problemi che il sondaggio non può vedere tornano più tardi, come attività fallita con un errorCode fra le nove classi pubbliche della piattaforma e un messaggio che spiega cosa è andato storto; l'importo trattenuto per l'attività viene restituito. Vedi Errori.

Il caso peggiore è ancora più silenzioso: pesi costruiti per un modello di base diverso possono caricarsi senza fallire e cambiare il risultato quasi per nulla. Non c'è nessun errore da leggere, solo un'immagine che somiglia a quella del modello di base.

Perciò verificate al momento del rilascio

Spostate la verifica fuori dal percorso della richiesta, dentro ciò che gira quando rilasciate:

  1. Raggiungibilità. Una richiesta HEAD verso ogni indirizzo di pesi che state per mandare in produzione. La piattaforma sonda ogni link quando l'attività viene creata, ma un link che oggi funziona può smettere di farlo la settimana prossima, e una verifica al rilascio se ne accorge prima del vostro traffico.
  2. Effetto. Una generazione per ogni nuovo file di pesi, confrontata con lo stesso prompt e lo stesso seme senza di esso. Se le due sono indistinguibili, il file non sta facendo quel che credete: accertatelo prima che un lotto intero vi si appoggi.
  3. Stabilità. I vostri indirizzi devono continuare a funzionare per tutto il tempo in cui restano in produzione. Un file di pesi che sparisce si trasforma in attività fallite senza causa evidente, giorni dopo il rilascio a cui verranno attribuite.

I consigli specifici di ciascun endpoint su questi campi — rispetto a cosa venga davvero misurato un valore, quale impostazione rallenti in modo percepibile una chiamata, quali input lo schema accetti ma finirete per rimpiangere — vivono in paramNotes, sul catalogo pubblico, indicizzati per nome di parametro e disponibili in tutte e dieci le lingue della documentazione. È prosa, mai una regola; a decidere cosa viene accettato resta inputSchema. Vedi Catalogo dei modelli e schemi.

Asincrono, come ogni altro modello multimediale

Gli endpoint immagine sono attività, non chiamate richiesta-risposta. Create un'attività, poi raccogliete il risultato per interrogazione o tramite webhook; queued e running non sono terminali, succeeded, failed ed expired lo sono. L'autorità sugli stati è Attività asincrone.

Le immagini finiscono prima dei video, il che invoglia a scrivere la chiamata come se fosse sincrona. Resistete: qui un'interrogazione limitata è abbastanza breve da sembrarlo, ma il codice deve comunque gestire un'attività ancora in corso quando il budget di attesa si esaurisce, e affidarlo a un webhook scala meglio che tenere aperta una richiesta. Il polling usa la disponibilità di lettura dell'account, indipendentemente dall'invio delle attività. Un polling eccessivo può far raggiungere il limite di frequenza delle interrogazioni sullo stato; consulta Limiti di frequenza.

Leggere il risultato

Un'attività riuscita elenca i suoi file in output.assets[]. Tre cose su quell'elenco negli endpoint immagine:

  • Può contenere più di un file. Quando num_outputs supera uno, ricevete una risorsa per immagine. Scrivete il gestore come un ciclo fin dall'inizio: un gestore che legge assets[0] butta via in silenzio tutto il resto che avete pagato.
  • A decidere l'estensione è mime, non il nome dell'endpoint né il prompt. Quando un endpoint espone output_format, è quel campo a scegliere la codifica; quando non lo espone, la codifica è propria dell'endpoint e mime è l'unico modo di sapere cosa avete ricevuto.
  • Un file ancora in copia porta pending: true e nessun url. I risultati vengono copiati nello storage di SpicyAPI prima di essere consegnati, e questo richiede qualche secondo dopo che l'attività ha segnalato il successo. Rileggete l'attività invece di trattare il collegamento mancante come un fallimento.

I collegamenti di download durano circa venti minuti e non richiedono la chiave API; per averne uno fresco rileggete l'attività. I file generati sono conservati al massimo quattordici giorni, meno se la conservazione del vostro account è più breve. Vedi Risultati e download e Conservazione e distruzione.

Leggere un rifiuto

Una richiesta rifiutata non costa nulla e non crea nulla, perciò l'endpoint di preventivo funge anche da validatore gratuito per il corpo che state per inviare.

Vengono segnalati tutti i problemi, non solo il primo. Il messaggio li unisce con ; , quindi un solo preventivo rifiutato può dirvi tutto ciò che non va:

prompt: is required; loras.0.path: is required; loras.0.scale: must be at most 4; bogus: is not a supported field

Un problema dentro un array indica l'elemento. Il puntatore è campo.indice.sottocampo, con indice a partire da zero:

loras.1.scale: must be at most 4

Passate quel puntatore a chi dovrà correggere. Con più elementi in gioco, sapere quale è fuori intervallo è la differenza fra una correzione di una riga e una bisezione.

Le forme che incontrerete:

MessaggioChe cosa significaCosa fare
X: is requiredX è nell'elenco required della radiceAggiungerlo. Sugli endpoint di modifica di solito è prompt o il campo dell'immagine
X: is not a supported fieldX non è in properties; i campi sconosciuti vengono rifiutati, mai ignoratiToglierlo, oppure usare la grafia di questo endpoint — vedi Dimensione in uscita
X: must be one of: …Il valore è fuori da enumIl messaggio elenca i valori ammessi; enum e default sono nello schema
X: must contain at least N itemsSotto minItemsUn array che l'endpoint richiede non può essere inviato vuoto
X: must contain at most N itemsSopra maxItemsLeggete il tetto di questo endpoint; non è lo stesso ovunque
X: must be an arrayIl valore non è del tipo dichiaratoRacchiudete l'elemento singolo in una lista
X: must be at most N charactersSopra maxLengthAnche i prompt hanno un tetto per endpoint
X.<n>.<sub>: is requiredUn sottocampo dell'elemento <n> è obbligatorio e non ha predefinitoAggiungetelo solo a quell'elemento
X.<n>.<sub>: is not a supported fieldLo schema dell'elemento imposta additionalProperties: falseTogliete quella chiave da quell'elemento
X.<n>.<sub>: must be at most NIl sottocampo è fuori dal proprio intervalloLimitatelo; gli intervalli sono per sottocampo, non per array

Se il rifiuto arriva dove vi aspettavate che funzionasse, la causa consueta è uno schema invecchiato. Rileggete il catalogo dopo un 400 e non tenete mai un'istantanea permanente — Mettere in cache il catalogo dice per quanto vale una copia. Il resto lo copre Risoluzione dei problemi.

Passi successivi

In questa pagina