HiAPI
Blog

Tutoriale

Come usare gpt-6.1-terra tramite l'API HiAPI: curl, Python e una richiesta funzionante

Oct 31, 2026
文章封面图:淡紫色与墨黑色两大平面色块以一条细紫色硬边相接,构图平静而对称。

Cosa serve davvero per una richiesta a gpt-6.1-terra

Ogni chiamata a gpt-6.1-terra su HiAPI si riduce a quattro cose: un URL, un'intestazione di autenticazione, l'identificatore del modello e il corpo della richiesta. Una volta che questi sono corretti, il resto dell'articolo è solo il modo di collegare le parti.

L'endpoint

Tutte le chiamate ai modelli partono dallo stesso URL di base su HiAPI. Non serve cambiare host per ogni modello: è il nome del modello nel corpo della richiesta a determinare il routing. Punta il tuo client all'URL di base di HiAPI e salvalo in una variabile, così devi modificarlo in un solo punto.

L'intestazione di autenticazione

L'autenticazione avviene tramite un bearer token passato nell'intestazione Authorization. Tale token è la chiave API emessa nel tuo account HiAPI. Non incorporarlo mai direttamente nel codice sorgente: leggilo da una variabile d'ambiente (ad esempio HIAPI_API_KEY), così lo stesso codice funziona in locale, in CI e in produzione.

L'identificatore del modello

Il corpo della richiesta deve specificare esplicitamente il modello: "model": "gpt-6.1-terra". La stringa è sensibile alle maiuscole e minuscole e deve corrispondere esattamente. Un nome di modello errato o troncato è una delle cause più comuni di rifiuto della richiesta, quindi trattalo come una costante nel codice, non come una stringa scritta al volo.

I campi del corpo della richiesta

Una richiesta porta come minimo il modello e il contenuto di input, cioè il prompt o i messaggi che vuoi che il modello elabori. Su questa base puoi aggiungere qualsiasi parametro opzionale supportato dal modello (suggerimenti sul formato di output, controllo della lunghezza, ecc.), ma inizia dal corpo minimo che produce una risposta valida. Un corpo minimo è più facile da diagnosticare di uno completamente equipaggiato e ti aiuta a capire se il fallimento è nel corpo della richiesta o nel collegamento delle parti.

C'è un punto strutturale che vale la pena interiorizzare subito: alcune risposte vengono restituite inline, altre come task che devi interrogare periodicamente. Più avanti vedremo come distinguerle, ma già ora progetta il tuo livello di richiesta in modo che la gestione della risposta possa passare da un modo all'altro senza riscritture.

Una richiesta curl funzionante, analizzata riga per riga

Di seguito un esempio completo di chiamata: esporta la chiave e incollalo nel terminale per eseguirlo. Usa l'URL di base di HiAPI e un corpo JSON minimo.

curl -sS -X POST "$HIAPI_BASE_URL/v1/chat/completions" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-6.1-terra",
    "messages": [
      {"role": "user", "content": "Riassumi il ciclo dell'acqua in tre frasi."}
    ]
  }'

Cosa fa ogni flag

-X POST imposta il metodo HTTP. Le chiamate ai modelli sono POST perché trasportano un corpo della richiesta.

-sS fa sì che curl nasconda le informazioni di avanzamento ma mostri comunque gli errori reali. Senza -S, una richiesta fallita potrebbe sembrare un silenzio totale.

-H "Authorization: Bearer $HIAPI_API_KEY" aggiunge le tue credenziali. Il prefisso Bearer e il singolo spazio che lo segue fanno parte dello schema di autenticazione; omettere uno dei due causa un errore di autenticazione che sembra non avere nulla a che fare con l'intestazione.

-H "Content-Type: application/json" dice al server che il corpo è JSON. Alcuni gateway tollerano un content type mancante, ma non contarci.

-d '...' è il corpo della richiesta. Gli apici singoli impediscono alla shell di interpolare il JSON. Se preferisci usare un file, sostituisci -d '...' con -d @payload.json e metti il JSON in un file che puoi validare e confrontare con un diff.

Due abitudini che fanno risparmiare tempo

Primo, quando si verifica un fallimento, aggiungi -i per stampare la riga di stato e le intestazioni della risposta. Una risposta con stato 200 ma corpo anomalo si interpreta in modo molto diverso da un 401 o un 422, e vorrai vedere subito questa differenza. Secondo, passa l'output attraverso un formattatore JSON (curl ... | python -m json.tool) per rendere leggibili i campi annidati della risposta. Entrambi i flag non hanno effetti collaterali e li userai spesso mentre metti a punto la chiamata.

Portare la chiamata in Python

La versione Python deve mantenere esattamente la semantica della chiamata curl: stesso URL, stesse intestazioni, stesso corpo, stessa modalità di analisi. Usare la libreria standard mantiene le dipendenze a zero e rende il porting immediatamente comprensibile.

import json
import os
import urllib.request

HIAPI_BASE_URL = os.environ["HIAPI_BASE_URL"].rstrip("/")
HIAPI_API_KEY = os.environ["HIAPI_API_KEY"]

def call_terra(prompt: str, model: str = "gpt-6.1-terra") -> dict:
    body = {
        "model": model,
        "messages": [{"role": "user", "content": prompt}],
    }
    req = urllib.request.Request(
        f"{HIAPI_BASE_URL}/v1/chat/completions",
        data=json.dumps(body).encode("utf-8"),
        headers={
            "Authorization": f"Bearer {HIAPI_API_KEY}",
            "Content-Type": "application/json",
        },
        method="POST",
    )
    with urllib.request.urlopen(req, timeout=120) as resp:
        return json.loads(resp.read().decode("utf-8"))

result = call_terra("Riassumi il ciclo dell'acqua in tre frasi.")
print(json.dumps(result, indent=2))

Note sul porting

Le intestazioni vengono costruite una volta sola all'interno dell'oggetto richiesta, in corrispondenza dei due flag -H. Il corpo è un dict Python, serializzato con json.dumps, il che è programmaticamente equivalente al corpo curl racchiuso tra apici singoli, e in più elimina i problemi di quoting sulla shell di Windows.

La funzione prende il modello come parametro con un valore predefinito, invece di ripetere il letterale ovunque. Così il nome del modello ha un'unica fonte di verità e puoi comunque sovrascriverlo deliberatamente.

Qui la gestione degli errori è volutamente omessa per mantenere leggibile il percorso principale. Nel codice reale, avvolgi la chiamata a urlopen e cattura le eccezioni HTTP, leggendo .read() dall'oggetto errore per vedere il messaggio del server. Il timeout è impostato esplicitamente; un blocco indefinito finirebbe per bloccare un worker.

Se preferisci httpx o requests, la mappatura è uno a uno: requests.post(url, headers=..., json=body, timeout=120). Il parametro json= serializza il dict per te. La semantica non cambia, ed è proprio questo il punto: la chiamata curl è il contratto, Python è solo la sua seconda forma di presentazione.

Scegliere tra chiamata singola e task asincrono

Non ogni richiesta è adatta all'attesa. La decisione dipende da quanto dura il lavoro e se il chiamante può permettersi di bloccarsi.

Quando va bene una chiamata singola

Richieste brevi e interattive — riassunti, classificazioni, piccole riscritture — dovrebbero essere normali chiamate sincrone. La risposta viene restituita inline, la analizzi e restituisci il risultato. È il percorso di codice più semplice e il più facile da ragionare nei test. Se un utente sta aspettando un'interfaccia, la sincronia è la forma prevista.

Quando trattarla come un task

Quando il lavoro è di lunga durata o produce molti artefatti, e un'interruzione della connessione significherebbe rifare un lavoro costoso, la chiamata va trattata come asincrona. In questi casi invii la richiesta, ricevi un identificatore di task e interroghi periodicamente finché non raggiunge uno stato finale. L'invio stesso ritorna rapidamente, quindi il tuo worker o il tuo gestore HTTP non resta occupato per tutta la durata.

Una regola pratica: se ti accorgi di impostare un timeout del client insolitamente lungo per "far riuscire la chiamata", è il segnale per passare ai task. Il timeout non è un meccanismo di pianificazione.

Rendere economico il passaggio

Non dividere la codebase in due client non correlati. Incapsula entrambi i percorsi dietro una funzione che restituisce un risultato immediato o un handle di task, e lascia che il chiamante richieda la modalità desiderata. Così la costruzione della richiesta — modello, autenticazione, corpo — resta condivisa e cambia solo la strategia di attesa. Questo significa anche che il debug è coerente: quando il corpo della richiesta è piccolo, puoi riprodurre in sincrono un fallimento asincrono.

Sii onesto sul tuo carico di lavoro, invece di indovinare il comportamento della piattaforma. Se le tue richieste di solito si completano in pochi secondi, resta in sincrono e salta la complessità. Il percorso asincrono vale per le richieste davvero lente, non per tutte.

Interrogare lo stato di completamento senza martellare l'API

Un ciclo di polling ha tre responsabilità: aspettare abbastanza a lungo da essere cortese, fermarsi prima o poi, e presentare chiaramente lo stato finale. Ecco una versione compatta.

import time

def poll_until_done(fetch_status, job_id, interval=3.0, timeout=600.0):
    deadline = time.monotonic() + timeout
    while True:
        status = fetch_status(job_id)
        state = status.get("status")
        if state in ("succeeded", "failed", "cancelled"):
            return status
        if time.monotonic() > deadline:
            raise TimeoutError(f"job {job_id} not finished after {timeout}s")
        time.sleep(interval)

Perché questa struttura

fetch_status è iniettato, quindi lo stesso ciclo funziona sia che interroghi via HTTP sia che interroghi una coda interna. Questo rende anche banale testarlo con un oggetto finto che restituisce due volte running e poi succeeded.

Il controllo dello stato è una whitelist esplicita di stati finali, invece del controllo di una singola stringa "done". Gli stati finali sono diversi — successo, fallimento, annullamento — e vuoi fermarti quando ne appare uno qualsiasi, non girare a vuoto fino al timeout perché ne stavi cercando uno solo.

La scadenza usa time.monotonic(), così le regolazioni dell'orologio di sistema non allungano né accorciano accidentalmente l'attesa.

Scegliere l'intervallo di polling

Parti da qualche secondo e resisti all'impulso di fare polling in un ciclo serrato. Intervalli inferiori al secondo consumano budget di richieste e aumentano il carico senza far completare il task più in fretta. Se prevedi task lunghi, valuta un backoff graduale — ad esempio aumentando l'intervallo dopo ogni controllo fino a un tetto. Mantieni il tetto moderato; l'obiettivo è ridurre le richieste, non creare zone cieche di minuti.

Il timeout non è opzionale

Imposta sempre un timeout, generoso rispetto al lavoro ma finito. Quando scatta, non restituire silenziosamente un risultato vuoto. Solleva un'eccezione, registra l'identificatore del task e lascia che il chiamante decida se riprovare — un task scaduto nel polling potrebbe essere ancora in esecuzione, e un nuovo invio cieco potrebbe duplicare il lavoro.

Scaricare e memorizzare l'output

Se la risposta contiene riferimenti ad artefatti — URL, identificatori di file o chiavi di archiviazione — trattali come temporanei. Possono scadere, possono essere limitati al tuo account e possono non sopravvivere ai tentativi. La copia persistente è quella che salvi tu.

Lo schema recupera-e-persisti

Quando un task è completato, leggi il riferimento all'artefatto dallo stato finale, recuperalo una volta e scrivilo in una destinazione che controlli: un percorso locale per gli script, un bucket di object storage per qualsiasi cosa vicina alla produzione. Registra l'identificatore del task insieme al file memorizzato, così puoi risalire dall'artefatto alla richiesta che l'ha prodotto.

def save_output(fetch_bytes, artifact_url, dest_path):
    data = fetch_bytes(artifact_url)
    with open(dest_path, "wb") as f:
        f.write(data)
    return dest_path

Pratiche per evitare perdite silenziose di dati

Scrivi su un percorso temporaneo e rinomina solo dopo il successo, così un download fallito non lascia mai un file che sembra valido ma è scritto solo a metà. Se la risposta espone il numero di byte o un checksum, verificalo. Per i file grandi, scrivi in streaming su disco invece di caricare l'intero payload in memoria.

Conserva anche la risposta grezza della chiamata di stato finale. Memorizzare JSON costa poco, e quando qualcuno chiede perché un file sembra sbagliato, un documento di stato con nome del modello, timestamp e id del task è il modo più rapido per ricostruire l'accaduto.

Infine, separa recupero ed elaborazione. Prima scarica e memorizza, poi trasforma. Se scarichi e analizzi nello stesso momento, un breve errore di rete sembra un bug di formattazione e passerai il pomeriggio a risolvere il problema sbagliato.

Diagnosticare i guasti comuni

La maggior parte dei guasti rientra in quattro categorie. Ognuna ha un rapido test di isolamento.

Errori di autenticazione

Sintomo: 401 o 403 prima che qualsiasi contenuto del corpo della richiesta diventi rilevante. Verifica che la chiave sia effettivamente caricata — stampa se la variabile d'ambiente è impostata, non il suo valore. Controlla il prefisso Bearer e lo spazio che lo segue. Conferma che il nome dell'intestazione sia scritto correttamente e che nessun proxy lo rimuova. Se la stessa chiave funziona in curl ma non nella tua applicazione, significa che la tua applicazione costruisce le intestazioni in modo diverso; confronta il dict letterale delle intestazioni con i flag curl.

Corpo della richiesta malformato

Sintomo: 400 o 422 con un messaggio relativo al corpo della richiesta. Valida il JSON prima di inviarlo — json.dumps serializza quasi qualsiasi cosa, inclusi dict che non intendevi costruire. Controlla virgole finali incollate da file di configurazione, contenuti che dovrebbero essere stringhe ma vengono passati come liste e campi obbligatori mancanti. Riduci al corpo minimo della prima sezione, poi aggiungi i campi uno alla volta finché non fallisce.

Nome del modello errato

Sintomo: rifiuto che assomiglia a una richiesta errata o a un messaggio di modello non trovato. La stringa del modello deve essere esattamente gpt-6.1-terra. Cause comuni: un valore predefinito obsoleto in una funzione di supporto, una variabile d'ambiente che contiene un valore diverso, o un errore di battitura dovuto al fatto di averlo ridigitato invece di copiarlo e incollarlo. Mantienilo come costante di modulo e cerca le duplicazioni con grep.

Timeout del polling

Sintomo: il tuo ciclo esce per timeout mentre il task è ancora vivo. Prima di tutto, verifica che l'identificatore del task che interroghi sia quello che hai inviato — stampare entrambi al momento dell'invio è il controllo più semplice. Poi controlla la tua lista di stati finali: uno stato che non avevi previsto fa girare il ciclo per sempre. Infine, controlla che intervallo e scadenza corrispondano alla durata reale dei task; una scadenza tarata su richieste brevi scatterà sempre su quelle lunghe. Al timeout, registra l'ultimo payload di stato, così sai in che stato era il task quando hai rinunciato.

Affronta i problemi in questo ordine — autenticazione, corpo, modello, polling — perché ogni livello maschera quello sottostante. Un'intestazione rotta fa sembrare sbagliato ogni corpo della richiesta.

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