Un modello video prende più di un prompt. Alcuni prendono un primo fotogramma e un ultimo fotogramma. Alcuni prendono una manciata di immagini di riferimento. Alcuni prendono una clip di riferimento, e poi si comportano diversamente per questo — silenziando la propria colonna sonora, o prendendo la durata della clip come durata del risultato. Alcuni prendono una clip audio. Ogni modello disegna quella busta diversamente, e la maggior parte delle nostre ne inviava solo una parte.
Ora c'è un unico posto che descrive la busta di ogni modello: quante immagini, se una clip è consentita e per quanti secondi, se l'audio è accettato, se l'audio ha bisogno di un'immagine accanto, se una clip silenzia il suono proprio del modello, se una clip fissa la durata. È stato letto dalla documentazione ufficiale dei provider, poi messo alla prova lo stesso giorno con render reali su chiavi reali — così sono state trovate entrambe le cose seguenti.
Un file senza estensione
Consegnare a un modello una clip tramite link è la via ovvia. Fallisce su diversi di essi, in un modo molto difficile da indovinare: il file arriva senza estensione, e il modello valida in base all'estensione, quindi rifiuta un mp4 perfettamente valido perché non è un mp4. Gli stessi byte inviati inline venivano accettati ovunque. Quindi ogni clip viene prima ricodificata in un formato standard e inviata inline, con un tetto di dimensione oltre il quale viene comunque usato il percorso link, con un avviso.
Un livello che nessuno stava ottenendo
Il secondo stava costando denaro ai clienti. Uno dei modelli video non ha alcun campo risoluzione — il suo livello di qualità si imposta tramite un campo con un altro nome — e la piattaforma davanti a esso scarta silenziosamente i campi che non riconosce. Così ogni render su quel modello dall'aggiunta tornava al livello predefinito mentre il cliente aveva pagato per il livello superiore. Nulla falliva. Il video arrivava. Semplicemente non era il video addebitato.
Il livello ora è derivato dalla risoluzione effettivamente tariffata, e confermato tramite sonda.
Seedance passa a una corsia diretta
Nello stesso tratto, l'intera famiglia Seedance è passata dall'aggregatore all'API propria del provider, con l'aggregatore mantenuto come fallback. Una riga modello, un nome, due set di tariffe — e il prezzo che ti viene indicato è quello che paghi anche quando la corsia primaria fallisce e gira il fallback, con il registro che annota quale corsia ha davvero fatto il lavoro, cosicché la nostra contabilità dei margini legga la verità invece del preventivo.
Una cosa da sapere come utente: quando alleghi una clip di riferimento su quella corsia, il provider addebita i secondi della clip in cima ai secondi del risultato. La stima nel modulo lo dice, e il numero indicato prima di eseguire è il numero addebitato.
Trovato lungo il percorso
Tre client API mancavano dell'unico attributo che rende visibile nel nostro container una classe con nome del provider. Due di essi erano client text-to-speech, invisibili dal giorno in cui sono stati aggiunti — motivo per cui due voci seminate nel catalogo non hanno mai potuto essere davvero servite. Un test di riflessione ora fa fallire la build alla prossima omissione di questo tipo, invece di aspettare che qualcuno noti una voce che non fa nulla.
E la pagina di ricerca web che avevamo rilasciato il giorno prima rispondeva 403 su ogni livello, su web e mobile. È bloccata da un permesso genitore; entrambi i seed di ruolo concedevano solo i suoi tre permessi figli, e da nessuna parte si deduce un genitore dai suoi figli. Altre quattro funzionalità avevano riparato questo a mano a un certo punto. Ora c'è un controllo che percorre l'albero dei permessi e fallisce dove un servizio è bloccato da un permesso che nessun ruolo possiede.