HiAPI
Blog

Tutoriel

Comment utiliser gpt-6.1-terra via l'API HiAPI : curl, Python et une requête qui fonctionne

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

Ce qu'une requête gpt-6.1-terra requiert réellement

Chaque appel à gpt-6.1-terra sur HiAPI se résume à quatre choses : une URL, un en-tête d'authentification, l'identifiant du modèle et le corps de la requête. Une fois ces éléments corrects, tout le reste de cet article n'est plus qu'une question de plomberie.

Le point de terminaison

Tous les appels de modèle passent par la même URL de base sur HiAPI. Vous n'avez pas besoin de changer d'hôte pour chaque modèle — c'est le nom du modèle dans le corps de la requête qui détermine le routage. Pointez votre client vers l'URL de base de HiAPI et conservez-la dans une variable, afin de ne la modifier qu'à un seul endroit.

L'en-tête d'authentification

L'authentification se fait par un jeton bearer transmis dans l'en-tête Authorization. Ce jeton est une clé API émise dans votre compte HiAPI. Ne la codez jamais en dur dans vos fichiers source ; lisez-la depuis une variable d'environnement (par exemple HIAPI_API_KEY), de sorte que le même code fonctionne en local, en CI et en production.

L'identifiant du modèle

Le corps de la requête doit spécifier explicitement le modèle : "model": "gpt-6.1-terra". Cette chaîne est sensible à la casse et doit correspondre exactement. Un nom de modèle erroné ou tronqué est l'une des causes les plus fréquentes de rejet de requête ; traitez-le donc comme une constante dans votre code, et non comme une chaîne écrite à la volée.

Les champs du corps de requête

Une requête porte au minimum le modèle et le contenu d'entrée — le prompt ou les messages que vous souhaitez faire traiter par le modèle. Au-delà de ce noyau, vous pouvez ajouter tout paramètre optionnel pris en charge par le modèle (indications de format de sortie, contrôle de longueur, etc.), mais commencez par le corps de requête minimal qui produit une réponse valide. Un corps minimal est plus facile à déboguer qu'un corps surchargé, et il vous aide à déterminer si l'échec vient de votre corps de requête ou de la plomberie.

Un point structurel vaut la peine d'être intégré dès maintenant : certaines réponses sont renvoyées en ligne, d'autres sous forme de tâches que vous devez interroger. Nous verrons plus loin comment distinguer les deux, mais concevez dès à présent votre couche de requête pour que le traitement des réponses puisse basculer entre les deux modes sans réécriture.

Une requête curl qui fonctionne, ligne par ligne

Voici un exemple d'appel complet, prêt à être collé dans un terminal après avoir exporté la clé. Il utilise l'URL de base de HiAPI et un corps JSON minimal.

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": "Résume le cycle de l'eau en trois phrases."}
    ]
  }'

Le rôle de chaque option

-X POST définit la méthode HTTP. Les appels de modèle sont des POST, car ils transportent un corps de requête.

-sS demande à curl de masquer la progression, tout en affichant les erreurs réelles. Sans -S, une requête échouée peut sembler n'être qu'un silence total.

-H "Authorization: Bearer $HIAPI_API_KEY" ajoute vos identifiants. Le préfixe Bearer et l'espace unique qui le suit font partie du schéma d'authentification ; omettre l'un ou l'autre provoque un échec d'authentification qui semble sans rapport avec l'en-tête.

-H "Content-Type: application/json" indique au serveur que le corps est du JSON. Certaines passerelles tolèrent un type de contenu absent, mais ne comptez pas sur cette tolérance.

-d '...' est le corps de la requête. Les guillemets simples empêchent le shell d'interpoler le JSON. Si vous préférez un fichier, remplacez -d '...' par -d @payload.json et placez le JSON dans un fichier que vous pouvez linter et comparer avec diff.

Deux habitudes qui font gagner du temps

Premièrement, en cas d'échec, ajoutez -i pour afficher la ligne de statut et les en-têtes de réponse. Une réponse qui renvoie 200 mais dont le corps est anormal ne se lit pas du tout comme un 401 ou un 422, et vous voudrez voir cette distinction immédiatement. Deuxièmement, passez la sortie dans un formateur JSON (curl ... | python -m json.tool) pour rendre lisibles les champs imbriqués de la réponse. Ces deux options sont sans effet de bord et vous les utiliserez souvent lorsque vous ajustez un appel.

Porter l'appel en Python

La version Python doit conserver exactement la sémantique de l'appel curl : même URL, mêmes en-têtes, même corps, même analyse. Utiliser la bibliothèque standard maintient le nombre de dépendances à zéro et rend le portage évident.

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("Résume le cycle de l'eau en trois phrases.")
print(json.dumps(result, indent=2))

Notes sur le portage

Les en-têtes sont construits en une fois dans l'objet requête, ce qui correspond aux deux options -H. Le corps est un dict Python, sérialisé avec json.dumps, ce qui équivaut programmatiquement au corps curl entre guillemets simples — et élimine le problème des guillemets sous le shell Windows.

La fonction prend le modèle comme paramètre avec une valeur par défaut, plutôt que de répéter le littéral partout. Cela donne une source unique de vérité pour le nom du modèle, tout en vous permettant de le surcharger volontairement.

La gestion des erreurs est volontairement omise ici pour garder le chemin nominal lisible. Dans du code réel, enveloppez l'appel urlopen et interceptez les exceptions d'erreur HTTP, puis lisez .read() sur l'objet d'erreur pour voir le message du serveur. Le délai d'attente est défini explicitement ; un blocage infini finira par suspendre un worker.

Si vous préférez httpx ou requests, la correspondance est directe : requests.post(url, headers=..., json=body, timeout=120). Le paramètre json= sérialise le dict pour vous. La sémantique ne change pas, et c'est précisément l'objectif — l'appel curl est le contrat, Python n'en est qu'une seconde représentation.

Choisir entre appel unique et tâche asynchrone

Toutes les requêtes ne se prêtent pas à l'attente. La décision dépend de la durée du travail et de la capacité de l'appelant à supporter un blocage.

Quand un appel unique convient

Les requêtes interactives courtes — résumé, classification, réécriture légère — devraient être de simples appels synchrones. La réponse est renvoyée en ligne, vous l'analysez, puis vous rendez la main. C'est le chemin de code le plus simple et le plus facile à raisonner dans les tests. Si un utilisateur attend devant une interface, le mode synchrone est la forme attendue.

Quand traiter comme une tâche

Lorsque le travail est de longue durée ou produit des artefacts volumineux, et qu'une interruption de connexion signifie refaire un travail coûteux, traitez l'appel comme asynchrone. Dans ces cas, vous soumettez la requête, recevez un identifiant de tâche, puis interrogez jusqu'à ce qu'elle atteigne un état terminal. La soumission elle-même revient rapidement, de sorte que votre worker ou votre gestionnaire HTTP n'est pas occupé pendant tout le processus.

Une règle pratique : si vous vous surprenez à définir un délai d'attente client anormalement long pour « faire réussir l'appel », c'est le signal de passer à une tâche. Un délai d'attente n'est pas un mécanisme d'ordonnancement.

Rendre le basculement peu coûteux

Ne divisez pas votre base de code en deux clients sans rapport. Enveloppez les deux chemins derrière une fonction qui renvoie soit un résultat immédiat, soit un handle de tâche, et laissez l'appelant demander le mode qu'il souhaite. Ainsi, la construction de la requête — modèle, authentification, corps — reste partagée, et seule la stratégie d'attente diffère. Cela signifie aussi que le débogage est cohérent : vous pouvez reproduire un échec asynchrone en synchrone lorsque le corps est petit.

Soyez honnête sur votre charge de travail plutôt que de deviner le comportement de la plateforme. Si vos requêtes se terminent généralement en quelques secondes, restez en synchrone et évitez la complexité. Le chemin asynchrone a de la valeur pour les requêtes réellement lentes, pas pour toutes.

Interroger l'état d'achèvement sans marteler l'API

Une boucle d'interrogation a trois responsabilités : attendre assez longtemps pour être poli, s'arrêter un jour, et présenter clairement l'état terminal. Voici une version compacte.

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)

Pourquoi cette structure

fetch_status est injecté, de sorte que la même boucle fonctionne que vous interrogiez via HTTP ou contre une file interne. Cela rend aussi trivial le test avec un faux objet qui renvoie deux fois running puis succeeded.

La vérification d'état est une liste blanche explicite d'états terminaux, plutôt que la vérification d'une seule chaîne « done ». Les états terminaux varient — succès, échec, annulation — et vous voulez vous arrêter dès que l'un d'eux apparaît, plutôt que de tourner jusqu'au délai d'attente parce que vous n'en avez cherché qu'un seul.

L'échéance utilise time.monotonic(), de sorte qu'un ajustement de l'horloge murale n'allonge ni ne raccourcit accidentellement l'attente.

Choisir l'intervalle d'interrogation

Commencez par quelques secondes et résistez à l'envie de boucler de façon serrée. Un intervalle inférieur à la seconde consomme votre budget de requêtes et augmente la charge, sans pour autant faire terminer la tâche plus vite. Si vous prévoyez des tâches longues, envisagez un backoff doux — par exemple, augmenter l'intervalle après chaque vérification jusqu'à un plafond. Gardez ce plafond raisonnable ; l'objectif est de réduire le nombre de requêtes, pas de créer des angles morts de plusieurs minutes.

Un délai d'attente n'est pas optionnel

Définissez toujours un délai d'attente, généreux par rapport au travail mais fini. Lorsqu'il se déclenche, ne renvoyez pas silencieusement un résultat vide. Levez une exception, consignez l'identifiant de tâche et laissez l'appelant décider de réessayer — une tâche dont l'interrogation a expiré peut encore être en cours, et une resoumission aveugle risque de dupliquer le travail.

Télécharger et stocker les sorties

Si la réponse contient des références d'artefacts — URL, identifiants de fichier ou clés de stockage — traitez-les comme éphémères. Elles peuvent expirer, être limitées à votre compte, et ne pas survivre à une nouvelle tentative. La copie durable est celle que vous enregistrez vous-même.

Le schéma récupérer puis persister

Lorsqu'une tâche se termine, lisez la référence d'artefact dans l'état terminal, récupérez-la une fois, et écrivez-la vers une destination que vous contrôlez : un chemin local pour un script, un bucket de stockage objet pour toute utilisation proche de la production. Consignez l'identifiant de tâche avec le fichier stocké, afin de pouvoir retracer l'artefact jusqu'à la requête qui l'a produit.

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

Pratiques pour éviter la perte silencieuse de données

Écrivez vers un chemin temporaire et renommez après succès, afin qu'un téléchargement échoué ne laisse jamais un fichier à moitié écrit qui semble valide. Si la réponse expose un nombre d'octets ou une somme de contrôle, vérifiez-les. Pour les gros fichiers, écrivez en flux sur le disque plutôt que de charger toute la charge utile en mémoire.

Conservez aussi la réponse brute de l'appel d'état final. Le stockage JSON coûte peu, et lorsqu'on vous demande pourquoi un fichier semble incorrect, le document d'état contenant le nom du modèle, l'horodatage et l'id de tâche est le moyen le plus rapide de reconstituer le déroulement.

Enfin, séparez la récupération du traitement. Téléchargez, stockez, puis transformez. Si vous analysez pendant le téléchargement, une brève erreur réseau ressemblera à un bug de formatage, et vous passerez l'après-midi à résoudre le mauvais problème.

Déboguer les défaillances courantes

La plupart des défaillances se rangent dans quatre catégories. Chacune a un test d'isolement rapide.

Erreurs d'authentification

Symptôme : un 401 ou 403 apparaît avant même que le contenu de votre corps de requête n'ait d'importance. Vérifiez que la clé est réellement chargée — affichez si la variable d'environnement est définie, pas sa valeur. Vérifiez le préfixe Bearer et l'espace qui le suit. Confirmez que le nom de l'en-tête est correctement orthographié et qu'aucun proxy ne le retire. Si la même clé fonctionne avec curl mais pas dans votre application, c'est que votre application construit les en-têtes différemment ; comparez le dict littéral d'en-têtes avec les options curl.

Corps de requête mal formé

Symptôme : un 400 ou 422 avec un message concernant le corps de la requête. Validez votre JSON avant l'envoi — json.dumps sérialise à peu près n'importe quoi, y compris un dict que vous n'aviez pas l'intention de construire. Vérifiez les virgules finales collées depuis un fichier de configuration, les contenus qui devraient être des chaînes mais sont passés comme listes, et les champs obligatoires manquants. Réduisez au corps minimal de la première section, puis rajoutez les champs un par un jusqu'à ce que cela échoue.

Nom de modèle erroné

Symptôme : un rejet qui ressemble à une requête invalide ou à un message de modèle introuvable. La chaîne du modèle doit être exactement gpt-6.1-terra. Causes fréquentes : une valeur par défaut obsolète dans une fonction utilitaire, une variable d'environnement contenant une valeur différente, ou une faute de frappe due à une ressaisie plutôt qu'à un copier-coller. Conservez-la comme constante de module et recherchez les doublons avec grep.

Expiration de l'interrogation

Symptôme : votre boucle se termine par un délai d'attente alors que la tâche est encore active. D'abord, confirmez que l'identifiant de tâche que vous interrogez est bien celui que vous avez soumis — afficher les deux au moment de la soumission est la vérification la plus simple. Ensuite, examinez votre liste d'états terminaux : un état que vous n'aviez pas prévu fera tourner la boucle indéfiniment. Enfin, vérifiez que votre intervalle et votre échéance correspondent à la durée réelle des tâches ; une échéance calibrée pour des requêtes courtes se déclenchera toujours sur des requêtes longues. Consignez la dernière charge utile d'état lors de l'expiration, afin de savoir dans quel état se trouvait la tâche lorsque vous avez abandonné.

Traitez ces points dans l'ordre — authentification, corps, modèle, interrogation — car chaque couche masque celle du dessous. Un en-tête cassé fera paraître tous les corps de requête comme erronés.

Articles similaires

Voir tous les articles →

Commencez avec des artefacts IA persistants

Obtenez votre clé API et appelez des modèles d'image, de vidéo et d'audio via une API unique basée sur les tasks.

Lire la documentation de l'API