Aller au contenu
Geb AI
Pour les développeurs

Construisez sur Geb. L'arabe d'abord, compatible OpenAI.

Une seule URL de base et une seule clé. Pointez votre SDK OpenAI vers Geb et obtenez des réponses solides en arabe et en plusieurs langues, le streaming, vos propres fonctions et des outils intégrés.

URL de basehttps://api.gebai.ca/v2

Connexion requise. Les comptes gratuits ont 500 appels par mois. Aucun frais par jeton.

Compatible avec les SDK OpenAI

Changez l'URL de base et la clé. Les complétions de chat, le streaming et l'appel de fonctions fonctionnent comme prévu.

L'arabe d'abord

Les réponses suivent la langue de votre utilisateur, en arabe standard moderne ou en arabe égyptien à votre demande.

Pensé pour les nouvelles tentatives

Clés d'idempotence, codes d'erreur clairs et une réponse à chaque appel, même quand une génération est interrompue.

1.Ce qu'est l'API, et ce qu'elle n'est pas

L'API de Geb donne à votre application le même modèle que celui qui répond dans Geb AI, par l'interface de complétions de chat que les bibliothèques clientes d'OpenAI parlent déjà. Pointez un SDK OpenAI vers https://api.gebai.ca/v2, utilisez une clé Geb, et cela fonctionne.

Geb appelle cette API API v2, et ses chemins d'URL commencent par /v2. Les clients existants qui appellent encore /v1 continuent de fonctionner : /v1/... est un alias qui atteint exactement les mêmes gestionnaires, avec le même comportement, les mêmes en-têtes, les mêmes erreurs et les mêmes quotas, et un appel ne compte qu'une fois, quel que soit le préfixe utilisé. Le nouveau code doit utiliser /v2.

Ce que vous obtenez

  • Complétions de chat sur POST /v2/chat/completions, en réponse complète ou en streaming par événements envoyés par le serveur.
  • Des réponses solides en arabe et en plusieurs langues. Geb répond dans la langue du dernier message de votre utilisateur, parmi 23 langues. L'arabe est une langue à part entière, pas une couche de traduction.
  • Vos propres fonctions grâce au champ standard tools (appel de fonctions).
  • Les outils intégrés de Geb, comme la recherche web, la météo, les générateurs de documents et de feuilles de calcul, lorsque vous les activez en les nommant.
  • Sortie structurée avec response_format (json_object et json_schema).
  • Entrée d'images dans les messages de l'utilisateur.
  • Contrôle du raisonnement avec des niveaux d'effort.
  • Conversations enregistrées que vous pouvez poursuivre par identifiant, séparées de toute mémoire du compte.
  • Règles de réponse (geb_acceptance) : déclarez des plages de mots, des expressions interdites et des exigences d'ancrage dans des sources, et Geb vérifie et affine la réponse avant de la renvoyer.
  • Aucune facturation au jeton. Votre forfait inclut un nombre d'appels par période de facturation (voir la section 10). Les jetons ne sont pas comptés.

Ce qu'elle n'est pas

  • Pas un proxy OpenAI. Chaque réponse vient du modèle propre de Geb. Votre requête n'est jamais transmise au modèle d'un autre fournisseur.
  • Sans mémoire. Les appels API ne lisent jamais la mémoire de votre compte Geb et n'y écrivent jamais. Un appel est construit à partir de votre seule requête (plus un fil de conversation enregistré si vous en demandez un, voir la section 5.5).
  • Uniquement des complétions de chat. Il n'existe pas de points de terminaison pour les embeddings, l'audio, la génération d'images, les fichiers, le traitement par lots, l'ajustement fin (fine-tuning), Assistants ou Responses.
  • Un seul choix. n, logprobs et l'ancien champ functions sont rejetés.
  • Certains champs d'échantillonnage sont acceptés et ignorés. stop, user, seed, temperature, top_p, presence_penalty, frequency_penalty, logit_bias, min_p, top_k et repetition_penalty ne provoquent jamais d'erreur, et ils n'ont aucun effet aujourd'hui.
  • Non appelable depuis un navigateur sur le site d'un tiers. L'API ne répond aux requêtes préalables cross-origin que pour les sites de Geb. Appelez-la depuis votre serveur, pas depuis du JavaScript côté client. Ne mettez jamais une clé dans une page web ou une application mobile.

2.Démarrage rapide

Votre premier appel en cinq minutes. Choisissez votre langage ; le choix est mémorisé pour tous les exemples de la page.

  1. Obtenez une clé (section 3). Elle ressemble à geb_live_ suivi de 43 caractères.
  2. Placez-la dans une variable d'environnement. Ne la collez jamais dans le code source.
  3. Faites un appel.
export GEB_API_KEY="geb_live_..."   # paste your key here, in your shell only

curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "geb",
    "messages": [
      {"role": "user", "content": "Greet me in Arabic and in English, one line each."}
    ]
  }'

Obtenir une clé

  1. Connectez-vous sur gebai.ca.
  2. Ouvrez Profil, puis Clés API.
  3. Créez une clé, copiez-la une seule fois, gardez-la sur votre serveur.
Obtenir votre clé API

Ce que vous recevez en retour

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1790000000,
  "model": "<model-id>",
  "choices": [
    {
      "index": 0,
      "message": { "role": "assistant", "content": "..." },
      "finish_reason": "stop"
    }
  ],
  "usage": { "prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0 }
}

model est l'identifiant du modèle qui a répondu, tel que le liste GET /v2/models. usage.prompt_tokens inclut les instructions de base propres à Geb, qui ouvrent chaque prompt.

Si vous avez reçu une 401, vérifiez la clé et l'en-tête Authorization (section 4). Si vous avez reçu une 429, lisez la section 10.

3.Obtenir une clé

Les clés sont créées par un compte Geb connecté, dans l'application web de Geb.

  1. Créez votre compte ou connectez-vous sur https://gebai.ca.
  2. Ouvrez Profil, puis Clés API (https://gebai.ca/profile/api-keys).
  3. Choisissez Créer une clé API. Donnez-lui un nom et, si vous le souhaitez, une durée de validité en jours (de 1 à 365).
  4. Copiez la clé immédiatement. Elle n'est affichée qu'une seule fois et ne peut pas être récupérée ensuite. Geb n'en conserve qu'un hachage.

Obtenir votre clé API

À savoir :

  • Un compte peut détenir jusqu'à 10 clés actives. Révoquez-en une pour libérer une place.
  • Révoquez une clé à tout moment depuis la même page. Une clé révoquée ou expirée répond 401 dès la requête suivante.
  • Chaque clé appartient au compte qui l'a créée. Les appels, le quota et les limites de débit sont comptés par compte, toutes clés confondues.
  • Le seul périmètre de clé est chat:write, qui couvre tous les points de terminaison de ce guide.
  • Tous les forfaits peuvent utiliser l'API, Gratuit compris : un compte Gratuit peut créer des clés et effectuer 500 appels par période de facturation. Le nombre d'appels inclus dans chaque forfait est indiqué à la section 10.

Gardez les clés sur votre serveur. Utilisez une clé par application ou par environnement, afin de pouvoir en révoquer une sans toucher aux autres.

4.Authentification

Envoyez la clé comme jeton porteur (bearer) à chaque requête.

Authorization: Bearer geb_live_...
curl https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY"

Une requête sans clé, avec un en-tête mal formé, avec un autre schéma (par exemple Basic), ou avec une clé inconnue, révoquée ou expirée, reçoit toujours la même réponse :

HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
x-request-id: 0f658b6d9742d46fec9ce4b984f99667

{"error":{"message":"A valid developer API key is required.","type":"invalid_api_key","param":null,"code":null,"request_id":"0f658b6d9742d46fec9ce4b984f99667"}}

Une 403 permission_error signifie que la clé est valide mais que le compte n'a pas le droit d'utiliser l'API (par exemple, le compte est suspendu).

5.Points de terminaison

Tous les points de terminaison se trouvent sous https://api.gebai.ca, utilisent JSON et exigent la clé porteuse. Le préfixe canonique est /v2. Les mêmes chemins avec le préfixe /v1 sont un alias pour les clients existants et se comportent de façon identique. Lorsqu'une réponse ou une erreur nomme un chemin, elle nomme le préfixe que vous avez utilisé.

Méthode et cheminObjet
GET /v2/modelsLister le modèle que vous pouvez appeler, avec ses capacités en direct.
GET /v2/models/{id}Récupérer ce modèle. Un identifiant inconnu donne 404.
POST /v2/chat/completionsCréer une complétion de chat, en réponse complète ou en streaming.
GET /v2/conversationsLister vos conversations enregistrées.
GET /v2/conversations/{id}Lire une conversation enregistrée.
DELETE /v2/conversations/{id}Supprimer une conversation enregistrée.

Les corps de requête peuvent être compressés en gzip ou deflate (Content-Encoding: gzip). Le corps de requête maximal est de 32 MiB après décompression.

5.1 Lister les modèles

curl https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY"

La réponse est une liste au format OpenAI. Chaque modèle porte des informations en direct sur ce que l'API peut faire à l'instant :

{
  "object": "list",
  "data": [
    {
      "id": "<model-id>",
      "object": "model",
      "created": 1755475200,
      "owned_by": "geb-ai",
      "context_window": 0,
      "modalities": { "input": ["text", "image"], "output": ["text"] },
      "capabilities": { "streaming": true, "tools": true, "vision": true, "reasoning": true },
      "reasoning": {
        "always_on": true,
        "efforts": ["low", "medium", "high", "ultra"],
        "default_effort": "<level>",
        "parameters": ["reasoning", "reasoning_effort", "thinking", "enable_thinking", "..."]
      },
      "limits": {
        "max_tokens": "answer_ceiling",
        "max_tokens_default": "derived_from_context_window",
        "long_non_streamed": "keepalive"
      },
      "geb_tools": ["web_search", "weather_forecast", "..."],
      "status": { "healthy": true, "state": "<state>" }
    }
  ]
}
  • context_window est le nombre de jetons que le modèle peut contenir dans une seule requête. Il vaut null lorsque Geb ne peut pas le mesurer pour le moment.
  • capabilities.vision et "image" dans modalities.input sont en direct : ils ne valent true que si une requête avec image serait acceptée à l'instant.
  • geb_tools est la liste exacte des noms d'outils intégrés qu'une requête peut nommer (section 7.2).
  • Les valeurs ci-dessus sont données à titre d'illustration. Lisez les valeurs réelles dans votre propre appel.

Aujourd'hui, l'API sert un seul modèle Geb. Le champ model d'une complétion est accepté pour la compatibilité avec OpenAI (n'importe quelle chaîne convient) et ne sélectionne pas un modèle plutôt qu'un autre. La réponse indique l'identifiant du modèle qui a réellement répondu.

5.2 Créer une complétion de chat

POST /v2/chat/completions

ParamètreTypeRemarques
modelchaîneFacultatif. Accepté pour la compatibilité, voir ci-dessus.
messagestableauObligatoire. Rôles system, user, assistant, tool. content est une chaîne ou, pour les messages user, un tableau de parties text et image_url.
streambooléentrue active le streaming par événements envoyés par le serveur (section 6).
max_tokensentierPlafond de la réponse visible. La réflexion est budgétée séparément (section 8).
tools, tool_choice, parallel_tool_callsVos propres fonctions (section 7.1).
response_formatobjet{"type":"json_object"} ou {"type":"json_schema", ...}.
stream_optionsobjet{"include_usage": true} ajoute un dernier fragment d'utilisation à un flux.
reasoning, reasoning_effort, thinking, enable_thinkingContrôle du raisonnement (section 8).
geb_toolsbooléen ou tableauOutils intégrés de Geb, désactivés par défaut (section 7.2).
geb_acceptanceobjetRègles de réponse (section 5.6).
conversation_idchaîne"new" démarre une conversation enregistrée, un identifiant en poursuit une (section 5.5).
locale"en", "ar", "fr"Indication facultative pour la langue de la réponse lorsque le message lui-même ne la détermine pas.

Acceptés mais ignorés (aucune erreur, aucun effet) : stop, user, seed, temperature, top_p, presence_penalty, frequency_penalty, logit_bias, min_p, top_k, repetition_penalty.

Rejetés avec 400 : n, logprobs, functions. geb_memory: true est également rejeté (memory_unavailable), car les appels API n'ont pas de mémoire.

Les champs inconnus sont des erreurs. Une propriété que Geb ne connaît pas, par exemple max_completion_tokens ou store, renvoie 400 avec un message tel que property store should not exist. Utilisez max_tokens. Si votre bibliothèque cliente ajoute des champs d'elle-même, supprimez-les.

Le corps JSON doit être du JSON valide. Les virgules finales, les commentaires, NaN et les autres erreurs de syntaxe sont rejetés avec 400 invalid_json_body. Une clé qui apparaît deux fois dans un même objet n'est pas une erreur : comme dans tout analyseur JSON standard, la dernière valeur l'emporte. N'envoyez pas de clés en double volontairement.

Les instructions propres à Geb ouvrent chaque prompt. Geb répond en tant que Geb, et ses règles de sécurité ne peuvent pas être contournées. Votre message system vient ensuite et cadre la conversation. Ces instructions sont comptées dans usage.prompt_tokens.

Langue de la réponse. Geb répond dans la langue du dernier message de votre utilisateur et suit un changement de langue en cours de conversation. Une instruction explicite telle que « répondez en français » l'emporte. Un message qui ne décide de rien (« ok », du code) conserve la langue du dernier message clair. Le code, les commandes et les arguments d'outils restent dans leur langue naturelle. Pour l'arabe, votre message system détermine le registre : demandez « arabe standard moderne » ou « arabe égyptien ». À défaut, les livrables écrits et les traductions sont en arabe standard moderne, et la conversation est en arabe égyptien. Une demande de traduction ne renvoie que la traduction.

Réponse. Une complétion normale ressemble à la réponse du démarrage rapide. finish_reason vaut stop (terminée), length (coupée à votre max_tokens, ou écourtée, voir la section 6.4), tool_calls (votre fonction est en cours d'appel) ou content_filter. Vérifiez-le toujours avant d'analyser du JSON dans une réponse.

r = client.chat.completions.create(
    model="geb",
    messages=[
        {"role": "system", "content": "You are a concise assistant for a travel app."},
        {"role": "user", "content": "اقترح لي خطة ليوم واحد في القاهرة"},
    ],
    max_tokens=800,
)
print(r.choices[0].message.content, r.choices[0].finish_reason, r.usage.total_tokens)

5.3 Sortie structurée

curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role":"user","content":"Extract: Layla, 34, lives in Giza."}],
    "response_format": {
      "type": "json_schema",
      "json_schema": {
        "name": "person",
        "strict": true,
        "schema": {
          "type": "object",
          "properties": {"name":{"type":"string"},"age":{"type":"integer"},"city":{"type":"string"}},
          "required": ["name","age","city"],
          "additionalProperties": false
        }
      }
    }
  }'
  • Les réponses json_schema sont conformes au schéma. Envoyez "strict": true pour que Geb vérifie d'abord votre schéma : chaque objet exige alors additionalProperties: false et chaque propriété doit figurer dans required, et un schéma qui ne peut pas satisfaire cette exigence est rejeté avec 400, en nommant le chemin fautif. Sans champ strict, Geb utilise votre schéma tel quel et ne le rejette pas : un schéma qui enfreint les règles strictes n'est donc pas une erreur, mais ses réponses ne sont contraintes que dans la mesure où le schéma l'est.
  • Vérifiez finish_reason. S'il vaut length, le JSON est incomplet. Un corps JSON coupé n'est jamais signalé comme stop.
  • Un appel response_format en streaming est retenu jusqu'à ce que Geb ait vérifié la réponse, puis livré (section 6.3). Des signaux de présence maintiennent la connexion ouverte entre-temps.
  • Les outils intégrés de Geb ne s'exécutent pas lors d'un appel response_format.

5.4 Entrée d'images

Les messages de l'utilisateur peuvent contenir des images sous forme de parties de contenu OpenAI. Les images sont envoyées au modèle de vision propre à Geb.

r = client.chat.completions.create(
    model="geb",
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "What is in this picture?"},
            {"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}},
        ],
    }],
)
  • Sources : URL https:// (récupérées par Geb) ou URI data:image/{png,jpeg,webp,gif};base64,.... Les URL http:// et les autres schémas sont rejetés.
  • Formats : PNG, JPEG, WebP, GIF (première image). Les octets doivent correspondre au type déclaré.
  • Limites : au plus 16 images par requête, 20 MB par image, 64 MB de données d'image décodées par requête. Envoyez les grandes images sous forme d'URL : le corps entier de la requête est plafonné à 32 MiB.
  • Les URL qui pointent vers des adresses privées ou internes sont refusées (image_url_forbidden).
  • Les images d'une requête sont présentées avec le dernier message de l'utilisateur, dans l'ordre. Un message contenant uniquement des images est valide. Les parties autres que text et image_url (audio, fichiers) sont rejetées.
  • Si le modèle de vision est indisponible à ce moment-là, vous recevez 503 vision_unavailable, jamais une supposition à partir du texte seul. GET /v2/models indique capabilities.vision: false dans le même état.

5.5 Conversations

Vous pouvez envoyer tout l'historique à chaque requête, comme avec n'importe quelle API de chat. Geb peut aussi enregistrer le fil pour vous.

  • Envoyez "conversation_id": "new". L'identifiant revient dans la réponse (conversation_id) et dans le dernier fragment du flux.
  • Envoyez cet identifiant lors des appels suivants pour poursuivre la conversation.
  • Si votre requête contient un message assistant ou tool, vous avez envoyé l'historique : vos messages sont utilisés exactement tels quels. Si elle ne contient que des messages system et user, Geb ajoute le fil enregistré au prompt, en commençant par les tours les plus récents, autant que la fenêtre de contexte le permet. Le nouveau tour est enregistré dans les deux cas.
  • Un fil enregistré n'est pas la mémoire du compte. Le chat ne la lit jamais, et aucun autre compte ne peut la lire.
RouteRésultat
GET /v2/conversations?limit=50&after=<cursor>{"object":"list","data":[{"id","object":"conversation","created_at","last_message_at","title"}],"has_more","next_cursor"}, de la plus récente à la plus ancienne.
GET /v2/conversations/{id}?limit=100&after=<cursor>La conversation avec messages: [{"role","content","created_at"}] du plus ancien au plus récent, plus message_count, has_more et next_cursor.
DELETE /v2/conversations/{id}{"id","object":"conversation.deleted","deleted":true}.

Un identifiant qui n'est pas le vôtre répond 404. Ces trois routes comptent comme des appels API dans votre quota, comme une complétion ; un appel qui échoue n'en consomme pas.

curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{"conversation_id":"new","messages":[{"role":"user","content":"Remember: my shop sells dates."}]}'

5.6 Règles de réponse (`geb_acceptance`)

Pour un travail où la réponse doit satisfaire une liste de contrôle, déclarez les règles. Geb rédige, vérifie, affine ce qui échoue, et renvoie la meilleure version avec un rapport. Les vérifications de règles ne sont pas facturées : l'appel reste un seul appel.

{
  "messages": [{"role": "user", "content": "Write a 300 word product summary from the sources below. ..."}],
  "geb_acceptance": {
    "version": 1,
    "rules": [
      {"type": "word_range", "path": "", "min": 250, "max": 350},
      {"type": "forbidden_phrases", "paths": ["/**"], "phrases": ["world-class", "cutting-edge"]},
      {"type": "claims_grounded", "paths": ["/**"]}
    ],
    "on_fail": "return_best_with_report"
  }
}
  • Types de règles : word_range, forbidden_phrases, no_repeated_paragraphs, cited_ids_named, min_named_sources, claims_grounded, no_copy_from, phrases_require_source. Un path est un pointeur JSON (JSON Pointer) dans la réponse ; pour une réponse en texte brut, utilisez "" ou /**.
  • La réponse porte x_geb_acceptance: {"passed": true, "rounds": 1}, ou passed: false avec une liste failures.
  • on_fail: "error_422" transforme un échec final en 422 geb_acceptance_failed.
  • Un contrat invalide donne 400 invalid_geb_acceptance, où param nomme la règle et rule_index sa position.
  • Un flux avec geb_acceptance est retenu jusqu'à la fin des vérifications (section 6.3).

5.7 En-têtes présents dans chaque réponse

En-têteSignification
x-request-idL'identifiant de cet appel, présent dans chaque réponse, réussie ou non. Il est égal à error.request_id en cas d'erreur. Citez-le au support.
x-geb-api-version2 avec le préfixe /v2 comme avec le préfixe /v1.
X-RateLimit-LimitLa limite par minute, 120 pour tous les forfaits (section 10.2).
X-Quota-Limit-Month, X-Quota-Remaining-Month, X-Quota-ResetVotre position dans le quota mensuel d'appels (section 10.1).
x-geb-reasoning-effort, x-geb-reasoning-adjustedLe niveau de raisonnement utilisé (section 8.4).

6.Streaming

Définissez "stream": true. La réponse est de type text/event-stream : des événements envoyés par le serveur, chacun étant une ligne data: <json> suivie d'une ligne vide.

curl -N https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{"stream":true,"stream_options":{"include_usage":true},
       "messages":[{"role":"user","content":"Count to five in Arabic."}]}'

6.1 Ordre des trames

  1. Une ligne de commentaire de remplissage au début (les commentaires commencent par :), qui vide les tampons des proxys. Ignorez-la.
  2. Un premier chat.completion.chunk dont le delta porte role: "assistant".
  3. Des fragments de contenu, chacun avec du texte dans delta.content.
  4. Un dernier fragment avec delta: {} et un finish_reason. Il porte aussi conversation_id lorsque vous utilisez des conversations.
  5. Si vous définissez stream_options.include_usage : un fragment de plus avec "choices": [] et un objet usage.
  6. data: [DONE].
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{"role":"assistant","content":"One"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{"content":", two"},"finish_reason":null}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: {"id":"chatcmpl-...","object":"chat.completion.chunk","model":"<model-id>","choices":[],"usage":{"prompt_tokens":0,"completion_tokens":0,"total_tokens":0}}

data: [DONE]

Les fragments dont le tableau choices est vide sont des métadonnées (utilisation, et extensions de Geb telles que x_geb_acceptance et x_geb_partial). Un analyseur strict doit les ignorer lors de la lecture du texte. Geb ne diffuse jamais en streaming le texte de son raisonnement.

6.2 Signaux de maintien de connexion

Pendant que Geb réfléchit ou vérifie une réponse, la connexion reste ouverte et Geb écrit une ligne de commentaire, : heartbeat, environ toutes les 15 secondes. Les commentaires sont ignorés par tous les analyseurs SSE. Si vous écrivez votre propre analyseur, ignorez toute ligne qui commence par :.

Les en-têtes de réponse d'un flux incluent x-request-id, vos en-têtes X-Quota-* (section 10) et les en-têtes de raisonnement (section 8). Si vous fermez la connexion, Geb cesse de générer.

6.3 Flux retenus

Avec response_format ou geb_acceptance, Geb vérifie la réponse avant de l'envoyer. Le flux s'ouvre avec le commentaire : geb is checking the reply before sending it, puis seulement des lignes : heartbeat, puis le contenu vérifié. Si vous avez besoin des jetons au fur et à mesure de leur génération, n'envoyez aucun de ces deux champs.

6.4 Comment un flux se termine

Un flux se termine toujours par data: [DONE], de l'une des trois manières suivantes.

  1. Complet. Un dernier fragment avec finish_reason valant stop (ou tool_calls), puis [DONE].
  2. Réponse partielle. Si la génération se bloque ou échoue après l'envoi d'une partie du texte, Geb termine le flux avec ce qu'il a envoyé : un fragment de métadonnées {"choices":[],"x_geb_partial":{"reason":"stalled","code":"geb_dispatch_stalled","complete":false,"chars":123}}, puis un dernier fragment avec finish_reason: "length", puis [DONE]. Traitez-le comme une réponse incomplète. reason vaut stalled, edge_deadline, deadline ou upstream_error. Les réponses partielles ne sont proposées que pour les appels en texte brut ; un appel avec tools ou response_format reçoit à la place la trame d'erreur ci-dessous.
  3. Erreur dans le flux. Si rien d'exploitable n'a été produit, une trame data: {"error": {...}} suivie de data: [DONE]. Le statut HTTP était déjà 200, l'erreur ne peut donc pas être un statut HTTP. Les SDK officiels d'OpenAI lèvent une erreur lorsqu'ils voient cette trame.

Un flux qui se ferme sans [DONE] signifie que la connexion a été perdue. Traitez-le comme un échec et faites une nouvelle tentative (utilisez une Idempotency-Key, section 9.3).

Les problèmes que Geb peut détecter avant de commencer, comme une mauvaise clé, une erreur de validation ou un quota épuisé, reviennent sous la forme de véritables statuts d'erreur HTTP, et non de 200.

6.5 Analyser un flux sans SDK

import json, os, requests

r = requests.post(
    "https://api.gebai.ca/v2/chat/completions",
    headers={"Authorization": f"Bearer {os.environ['GEB_API_KEY']}"},
    json={"stream": True, "messages": [{"role": "user", "content": "Hello"}]},
    stream=True,
)
r.raise_for_status()
done = False
for raw in r.iter_lines(decode_unicode=True):
    if not raw or raw.startswith(":"):
        continue                      # blank line or keepalive comment
    payload = raw[len("data:"):].strip()
    if payload == "[DONE]":
        done = True
        break
    frame = json.loads(payload)
    if "error" in frame:
        raise RuntimeError(frame["error"])
    for choice in frame.get("choices", []):
        print(choice["delta"].get("content") or "", end="")
if not done:
    raise RuntimeError("stream ended without [DONE]; retry")

6.6 Longs appels sans streaming

Un appel sans streaming qui s'exécute encore après environ 70 secondes reçoit ses en-têtes 200 plus tôt, suivis d'un seul caractère espace environ toutes les 25 secondes jusqu'à l'arrivée du corps JSON. Un espace en tête est du JSON valide : JSON.parse, fetch().json(), Python requests et les SDK OpenAI lisent donc le corps sans modification. La réponse porte x-geb-keepalive: on lorsque cela s'est produit.

Si un appel échoue après l'envoi de ces en-têtes, le statut reste 200 et le corps est l'objet d'erreur ; lorsque x-geb-keepalive: on est présent, vérifiez donc la présence d'une clé error au niveau supérieur. Envoyez l'en-tête de requête x-geb-keepalive: off pour refuser ce mécanisme et obtenir à la place une 504 geb_edge_deadline au bout d'environ 95 secondes.

Pour les générations longues, utilisez le streaming. Une réponse en streaming n'a pas de limite de durée totale tant qu'elle progresse.

7.Appels d'outils et appel de fonctions

Il existe deux types d'outils différents. Ils sont indépendants et peuvent être envoyés ensemble.

tools (vos fonctions)geb_tools (outils intégrés de Geb)
À qui appartiennent les fonctionsÀ vousÀ Geb : recherche web, météo, documents, code
Qui les exécuteVousGeb
Effets de bordAucun du côté de GebRéels (par exemple un fichier généré)
Par défautUtilisées lorsque vous les envoyezDésactivés

Si vous envoyez les deux, le modèle voit vos fonctions, les appels qu'il renvoie sont à exécuter par vous, et l'exécuteur d'outils propre à Geb ne s'exécute pas pour ce tour.

7.1 Vos fonctions (`tools`)

Vous décrivez vos fonctions, Geb décide quand en appeler une, et vous l'exécutez puis renvoyez le résultat. C'est le protocole standard d'OpenAI : les SDK fonctionnent donc sans modification.

curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "parallel_tool_calls": false,
    "messages": [{"role":"user","content":"What is the weather in Alexandria?"}],
    "tools": [{
      "type": "function",
      "function": {
        "name": "get_weather",
        "description": "Current weather for a city.",
        "parameters": {
          "type": "object",
          "properties": {"city": {"type": "string"}},
          "required": ["city"]
        }
      }
    }]
  }'

Le modèle répond avec finish_reason: "tool_calls" et message.content valant null :

{
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": null,
      "tool_calls": [{
        "id": "chatcmpl-tool-9d22d13b24ef97ed",
        "type": "function",
        "function": { "name": "get_weather", "arguments": "{\"city\": \"Alexandria\"}" }
      }]
    },
    "finish_reason": "tool_calls"
  }]
}

Exécutez la fonction, puis envoyez une deuxième requête contenant votre historique, le tour de l'assistant exactement tel que reçu, et un message tool par appel :

import json

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Current weather for a city.",
        "parameters": {
            "type": "object",
            "properties": {"city": {"type": "string"}},
            "required": ["city"],
        },
    },
}]
messages = [{"role": "user", "content": "What is the weather in Alexandria?"}]

first = client.chat.completions.create(
    model="geb", messages=messages, tools=tools, parallel_tool_calls=False
)
call = first.choices[0].message.tool_calls[0]
result = {"city": "Alexandria", "temp_c": 27, "sky": "clear"}   # your code runs here

messages.append(first.choices[0].message)
messages.append({
    "role": "tool",
    "tool_call_id": call.id,
    "name": call.function.name,
    "content": json.dumps(result),
})
final = client.chat.completions.create(model="geb", messages=messages, tools=tools)
print(final.choices[0].message.content)

Règles :

  • tools[].type doit valoir "function". function.name correspond à ^[a-zA-Z0-9_-]{1,64}$ et doit être unique. function.parameters est un objet JSON Schema. Au plus 128 outils, et au plus 128 KiB sérialisés.
  • function.arguments est une chaîne encodée en JSON, dans les deux sens. Dans un message tool, content doit être une chaîne : sérialisez vous-même votre résultat.
  • Répondez à chaque appel. Si un tour de l'assistant a émis trois appels, envoyez trois messages tool avant la complétion suivante. Sinon, vous obtenez 400 unanswered_tool_call qui nomme les identifiants manquants. C'est délibéré : sans réponse, un modèle peut inventer les résultats manquants.
  • tool_choice : omis ou "auto" laisse le modèle décider, "none" interdit les appels, "required" exige au moins un appel, et {"type":"function","function":{"name":"..."}} n'autorise que cette fonction. Avec "required" ou une fonction nommée, si le modèle répond en prose au lieu d'appeler, Geb le relance une fois avant de répondre. Cela coûte un passage de modèle supplémentaire en latence, et non un second appel décompté de votre quota. Si le modèle répond encore en prose, vous recevez cette prose avec finish_reason: "stop" : vérifiez donc finish_reason avant de lire tool_calls. Avec un choix nommé, Geb filtre aussi la réponse sur cette fonction. Si vous avez besoin de la garantie la plus forte, utilisez "required" avec un catalogue d'une seule fonction. Sur un flux avec "required" ou une fonction nommée, le texte est retenu jusqu'à la fin du tour puis envoyé, de sorte qu'une réponse en prose arrive d'un seul bloc à la fin.
  • Envoyez "parallel_tool_calls": false sauf si vous voulez plusieurs appels par tour. Vous obtenez ainsi exactement un appel propre par tour.
  • Geb supprime les appels en double identiques octet pour octet et écarte un dernier appel coupé par length. Les arguments sont sinon transmis tels quels, y compris des arguments qui ne sont pas du JSON valide : validez-les donc avant de les utiliser.
  • Le streaming fonctionne. Les appels d'outils arrivent sous forme de trames standard delta.tool_calls (une trame d'ouverture avec id et name, puis des fragments d'arguments, accumulés par index). Ils arrivent ensemble à la fin du tour, sans être entremêlés à la génération. Le résultat accumulé est identique à celui d'un flux OpenAI.
  • Un aller-retour d'outil représente deux requêtes, et chacune compte comme un appel.

7.2 Outils intégrés de Geb (`geb_tools`)

Par défaut, un appel API est un simple appel au modèle : une génération, aucun outil, aucun effet de bord. Activez les outils avec geb_tools.

ValeurComportement
absent, false, []Aucun outil Geb. C'est le comportement par défaut.
trueTous les outils Geb disponibles à l'instant. Le modèle décide quand les utiliser.
["web_search", "weather_forecast"]Seulement les outils nommés. Le modèle voit exactement ceux-là, et l'appel à tout autre est refusé.
curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "messages": [{"role":"user","content":"What did the ECB decide at its last meeting?"}],
    "geb_tools": ["web_search"]
  }'

Les noms publics des outils (la liste en direct pour votre appel se trouve dans geb_tools sur GET /v2/models) :

NomCe qu'il fait
web_searchRecherche web : titres, liens et extraits.
web_grounded_searchRecherche web qui lit aussi les premières pages, pour des réponses avec citations.
url_fetchRécupère et lit une URL.
polinex_newsActualités récentes du flux Polinex pour les pays qu'il couvre.
weather_forecastConditions actuelles et prévisions pour un lieu.
currency_convertConversion de devises en direct.
wikipedia_summaryRésumé d'un article Wikipedia.
places_searchLieux et commerces près d'un emplacement.
geocodeDe l'adresse aux coordonnées, et inversement.
directionsUn itinéraire entre des lieux.
distance_matrixDistance et durée de trajet entre plusieurs points.
static_mapUne image de carte pour un lieu.
code_executeExécute du Python dans un bac à sable isolé. Les fichiers qu'il écrit sont renvoyés sous forme de liens.
generate_text_fileProduit un fichier texte (md, csv, json, html et autres) et renvoie un lien de téléchargement.
build_documentConstruit un document Word (.docx) et renvoie un lien de téléchargement.
build_workbookConstruit un classeur Excel (.xlsx) et renvoie un lien de téléchargement.
build_slidesConstruit une présentation PowerPoint (.pptx) et renvoie un lien de téléchargement.
  • Un nom que Geb ne reconnaît pas, ou qui est désactivé, renvoie 400 invalid_geb_tools avec la liste des noms valides dans error.valid_geb_tools. Au plus 32 noms.
  • Avec une liste, la restriction est appliquée deux fois : le modèle ne voit que les outils nommés, et tout appel à un autre outil n'est pas exécuté (le modèle reçoit une erreur d'outil et répond sans lui).
  • Les trois outils build_* sont séparés volontairement. Nommer build_document n'autorise qu'un fichier Word.
  • La construction de documents part de votre propre texte d'instruction, jamais de mots désignant un format de fichier dans un contenu que vous avez collé. Si la seule mention de « xlsx » ou de « slides » se trouve dans un bloc de code ou dans un long texte collé, Geb répond en texte.
  • Les outils peuvent prendre plus de temps qu'une complétion simple. Les fichiers produits par les outils sont stockés sous votre compte et renvoyés sous forme de liens à durée limitée.
  • La mémoire de votre compte, l'historique des chats, les connecteurs (Google, Microsoft, Dropbox, etc.) et les autres outils du compte ne sont pas disponibles via l'API.

8.Raisonnement

Geb réfléchit toujours avant de répondre. Vous choisissez l'intensité. Geb ne renvoie pas le texte de son raisonnement ; vous recevez la réponse, et les jetons de réflexion sont inclus dans usage.completion_tokens.

8.1 Niveaux

NiveauSignification
lowRéflexion la plus courte.
mediumLe niveau par défaut pour les questions plus difficiles.
highRéflexion longue. Correspond au réglage maximal du modèle.
ultraLe réglage maximal du modèle.

GET /v2/models liste les niveaux (reasoning.efforts) et la valeur par défaut du serveur (reasoning.default_effort).

8.2 Ce que signifie « raisonnement désactivé »

La réflexion n'est jamais désactivée. Chaque variante « off » ("off", "none", false, "disabled", 0) laisse la réflexion active avec un budget de réflexion de zéro jeton : le modèle clôt aussitôt son raisonnement et commence la réponse. Vous obtenez une réponse normale, sans étape de raisonnement. Utilisez-le pour les requêtes simples et sensibles à la latence. Pour les problèmes difficiles, préférez medium ou high.

8.3 Comment le demander

Utilisez la forme que votre client envoie déjà. Aucune n'est jamais rejetée.

ParamètreExemples
reasoning"high", "off", "on", {"effort": "medium"}
reasoning_effort"low", "medium", "high", "ultra", "minimal", "none", "xhigh"
thinking{"type": "enabled", "budget_tokens": 3000}, {"type": "disabled"}, true
enable_thinkingtrue, false
chat_template_kwargs{"enable_thinking": false}, {"reasoning_effort": "high"}
extra_body{"reasoning_effort": "medium"}, pour les clients qui envoient extra_body tel quel

Correspondances : minimal devient low. xhigh, max et maximum deviennent ultra. Une valeur budget_tokens correspond au niveau le plus proche (jusqu'à 1024, low ; jusqu'à 2048, medium ; jusqu'à 4096, high ; au-delà, ultra). Un niveau inconnu (par exemple "turbo") utilise la valeur par défaut du serveur.

Si vous envoyez plusieurs formes qui se contredisent, la première de cette liste qui nomme un niveau l'emporte : reasoning, reasoning_effort, thinking, enable_thinking, chat_template_kwargs, extra_body. Une forme qui ne fait qu'activer la réflexion ne remplace jamais un niveau nommé ailleurs.

curl https://api.gebai.ca/v2/chat/completions \
  -H "Authorization: Bearer $GEB_API_KEY" -H "Content-Type: application/json" \
  -d '{"reasoning_effort":"high","messages":[{"role":"user","content":"Plan a 3-city sales route to minimise travel."}]}' -i

8.4 Ce qui revient

En-tête de réponseValeur
x-geb-reasoning-effortLe niveau utilisé, ou default si vous n'en avez envoyé aucun.
x-geb-reasoning-adjustedPrésent lorsqu'une valeur a été convertie ou ignorée, par exemple enable_thinking=false->budget 0 (thinking stays on; think block closed).

8.5 max_tokens et réflexion

max_tokens est le plafond de la réponse visible. La réflexion est budgétée séparément, en plus de celui-ci : un petit max_tokens n'affame donc pas le raisonnement, et Geb ne multiplie jamais votre nombre. Si la réponse atteint le plafond, elle est coupée à cet endroit et finish_reason vaut length. Lorsque vous ne définissez pas max_tokens, le budget de la réponse est déduit de la fenêtre de contexte. usage.completion_tokens inclut les jetons de réflexion.

9.Erreurs et nouvelles tentatives

9.1 L'enveloppe

Chaque réponse d'erreur d'une route /v2 (et de son alias /v1) est du JSON, dans un seul format :

{
  "error": {
    "message": "A human-readable explanation.",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "invalid_json_body",
    "request_id": "0f658b6d9742d46fec9ce4b984f99667"
  }
}
  • type est la classe de l'erreur. Branchez votre logique sur type et code, jamais sur la formulation de message.
  • param nomme le champ fautif lorsqu'un champ est en cause, sinon null.
  • code est un motif plus précis, lisible par une machine, ou null.
  • request_id est toujours présent et est aussi envoyé dans l'en-tête de réponse x-request-id. Chaque réponse porte x-request-id, y compris les réponses réussies. Citez-le lorsque vous contactez le support. Envoyez votre propre x-request-id si vous le souhaitez ; Geb peut tout de même renvoyer son propre identifiant de trace.
  • Certaines erreurs ajoutent retry_after_ms au niveau supérieur, à côté de error, et Retry-After comme en-tête (en secondes).
  • Les échecs survenant après le début d'un flux arrivent dans le flux (section 6.4). Les échecs survenant après le début d'un long appel sans streaming arrivent sous forme de corps 200 avec une clé error (section 6.6).
  • Une requête vers un chemin qui n'existe pas renvoie une 404 simple {"message", "error", "statusCode"}, et non cette enveloppe. Si vous la voyez, vérifiez l'URL.

9.2 Catalogue

HTTPtypecodeSignification et action à mener
400invalid_request_errornullÉchec de validation : propriété inconnue, mauvais type, messages vide, champ non pris en charge (n, logprobs, functions), message trop volumineux. Corrigez la requête.
400invalid_request_errorinvalid_json_bodyLe corps n'est pas du JSON strict.
400invalid_request_errorinvalid_content_encodingLe corps n'est pas du gzip ou du deflate valide pour son Content-Encoding.
400invalid_request_errorinvalid_geb_toolsgeb_tools incorrect. error.valid_geb_tools liste les noms acceptés.
400invalid_request_errorinvalid_geb_acceptancegeb_acceptance incorrect. param et rule_index localisent la règle.
400invalid_request_errormemory_unavailable, invalid_geb_memorygeb_memory: true, ou une valeur non booléenne. Les appels API n'ont pas de mémoire.
400invalid_request_errorunsupported_tool_choice_mode, unknown_tool, duplicate_tool_name, too_many_tools, tools_too_large, invalid_type, invalid_value, missing_required_parameterUn problème de tools ou de tool_choice. param indique le chemin, par exemple tools[2].function.name.
400invalid_request_errorunknown_tool_call_id, unanswered_tool_callUn message tool ne correspond à aucun appel antérieur, ou un appel n'a pas de résultat.
400invalid_request_errorinvalid_content, unsupported_content_part, invalid_image_url, unsupported_image_type, invalid_image, too_many_images, image_too_large, images_too_large, image_url_forbidden, image_fetch_failed, image_fetch_timeoutUn problème d'image ou de partie de contenu (section 5.4).
401invalid_api_keynullClé absente, mal formée, inconnue, révoquée ou expirée. Ne réessayez pas.
403permission_errornullLa clé est valide mais le compte n'a pas le droit d'utiliser l'API. Ne réessayez pas.
404not_found_errornullIdentifiant de modèle inconnu lors d'une récupération, ou conversation qui n'est pas la vôtre.
409idempotency_key_reuse, idempotent_request_in_progressle même nom que typeMême Idempotency-Key avec un corps différent, ou le premier appel est encore en cours (section 9.3).
413invalid_request_errorrequest_too_largeLe corps décompressé dépasse la limite de 32 MiB.
415invalid_request_errorunsupported_content_encodingContent-Encoding autre que gzip ou deflate.
422invalid_request_errorgeb_acceptance_failedon_fail: "error_422" et la réponse ne respecte toujours pas vos règles. Non comptée dans votre quota.
429rate_limit_exceedednullLimite de rafale par minute. Attendez Retry-After secondes.
429rate_limit_errorgeb_concurrency_limitTrop de requêtes simultanées pour votre compte. Attendez Retry-After, ou la fin de l'une d'elles.
429quota_exceedednullQuota mensuel d'appels épuisé. La seule solution est d'attendre la réinitialisation, ou de changer de forfait.
502server_errorempty_answerLe modèle a terminé sans réponse. Réessayez immédiatement : la même requête réussit généralement. retry_after_ms vaut 0.
503server_busynull ou maintenanceGeb est occupé ou brièvement indisponible. Attendez Retry-After (environ 5 secondes lorsqu'il est occupé, jusqu'à 120 en cas de maintenance).
503server_errorvision_unavailableLe modèle de vision est indisponible pour le moment. Réessayez plus tard ou envoyez sans images.
503server_errordegenerate_outputLa réponse a été coupée pour des raisons de qualité et aucune nouvelle tentative propre n'était possible. Réessayez.
503server_errorgeb_instance_shutdownL'instance du serveur redémarre. Réessayez immédiatement.
504server_errorgeb_edge_deadline, geb_dispatch_stalled, geb_dispatch_deadlineLa requête a pris trop de temps ou n'a pas progressé. Réessayez avec "stream": true.
5xxserver_errornullTout le reste. Le message est volontairement générique. Réessayez avec un délai croissant et citez request_id si le problème persiste.

Sur un flux, les mêmes erreurs arrivent sous forme de trame d'erreur dans le flux, plus geb_incomplete_stream (la génération s'est arrêtée avant d'être terminée ; réessayez).

9.3 Conseils pour les nouvelles tentatives

  • Ne réessayez jamais 400, 401, 403, 404, 409 idempotency_key_reuse, 413, 415, 422. La requête doit changer.
  • Réessayez après l'attente pour 429 rate_limit_exceeded et geb_concurrency_limit (utilisez Retry-After), et pour 503 server_busy.
  • N'insistez pas sur 429 quota_exceeded. Lisez reset_at dans le corps ou X-Quota-Reset, et arrêtez-vous jusque-là.
  • Réessayez immédiatement, une ou deux fois pour 502 empty_answer et 503 geb_instance_shutdown.
  • Passez au streaming, puis réessayez pour 504.
  • Ajoutez un délai exponentiel avec variation aléatoire (jitter) pour les autres 5xx, avec un plafond de quelques tentatives.
  • Envoyez toujours une Idempotency-Key sur les appels que vous pourriez réessayer. C'est une chaîne quelconque d'au plus 255 caractères, unique pour chaque requête logique. Le premier appel s'exécute normalement. Une répétition avec la même clé et le même corps renvoie la réponse enregistrée (Idempotency-Replayed: true), sans nouvelle génération et sans consommer de quota, pendant 24 heures. La réponse rejouée a le même contenu, les mêmes champs et les mêmes valeurs que la première réponse ; l'ordre des clés à l'intérieur de l'objet JSON peut différer : comparez donc le JSON analysé, pas les octets. La même clé avec un corps différent donne 409 idempotency_key_reuse. Une répétition pendant que le premier appel est encore en cours donne 409 idempotent_request_in_progress : attendez, ne bouclez pas. Un flux qui a échoué en cours de route n'est pas enregistré, donc une nouvelle tentative s'exécute à nouveau. L'en-tête Idempotency-Status indique stored, replayed ou disabled.
  • Réglez généreusement le délai d'expiration de votre client, ou utilisez le streaming. Les SDK OpenAI envoient leur délai d'expiration dans un en-tête, et Geb renvoie la meilleure réponse partielle (finish_reason: "length", x_geb_partial) plutôt que rien lorsque votre propre délai approche.

Chaque appel se termine par l'une de trois issues : la réponse, une réponse partielle marquée finish_reason: "length" avec x_geb_partial, ou une erreur structurée. Une réponse partielle sans streaming ressemble à ceci :

{
  "choices": [{"index": 0, "message": {"role": "assistant", "content": "..."}, "finish_reason": "length"}],
  "x_geb_partial": {"reason": "stalled", "code": "geb_dispatch_stalled", "complete": false, "chars": 812}
}

9.4 Une fonction d'aide pour les nouvelles tentatives

import os, random, time, uuid, requests

URL = "https://api.gebai.ca/v2/chat/completions"
HEADERS = {"Authorization": f"Bearer {os.environ['GEB_API_KEY']}"}

def complete(body, attempts=5):
    key = str(uuid.uuid4())                       # same key on every attempt
    for n in range(attempts):
        r = requests.post(URL, json=body, headers={**HEADERS, "Idempotency-Key": key}, timeout=620)
        if r.ok:
            data = r.json()
            if "error" in data:                   # error after a keepalive commit
                raise RuntimeError(data["error"])
            return data
        err = r.json().get("error", {})
        code, kind = err.get("code"), err.get("type")
        if r.status_code in (400, 401, 403, 404, 413, 415, 422) or kind == "quota_exceeded":
            raise RuntimeError(f"{r.status_code} {kind}/{code}: {err.get('message')} ({err.get('request_id')})")
        if code in ("empty_answer", "geb_instance_shutdown"):
            wait = 0
        elif r.status_code == 504:
            raise RuntimeError("timed out: retry with stream=True")
        else:
            wait = float(r.headers.get("Retry-After", 2 ** n)) + random.random()
        time.sleep(wait)
    raise RuntimeError("gave up after retries")

10.Limites et quotas

10.1 Quota mensuel d'appels

Tous les forfaits incluent l'API, Gratuit compris, avec un nombre d'appels API par période de facturation. Les jetons ne sont pas comptés et il n'y a aucune facturation au jeton.

ForfaitAppels par période
Gratuit500
Basic1 500
Pro10 000
Master100 000
DeveloperIllimité
  • La période est la période de facturation de votre abonnement, ou le mois civil (UTC) si vous n'avez pas d'abonnement.
  • Seuls les appels réussis comptent. Un appel qui échoue ne consomme pas de quota. Une réponse rejouée à partir d'une Idempotency-Key ne consomme pas de quota. Les routes de conversation comptent comme des appels. Un aller-retour d'outil compte pour deux appels.
  • Au-delà du quota, l'API répond 429 quota_exceeded avec les détails :
{
  "error": {
    "type": "quota_exceeded",
    "message": "Monthly API request quota reached: your Free plan includes 500 calls per billing period and 500 have been used. The quota resets at 2026-11-01T00:00:00.000Z.",
    "tier": "free",
    "limit": 500,
    "used": 500,
    "remaining": 0,
    "reset_at": "2026-11-01T00:00:00.000Z",
    "request_id": "..."
  }
}

Chaque réponse réussie indique votre position dans des en-têtes : X-Quota-Limit-Month, X-Quota-Remaining-Month et X-Quota-Reset. Une 429 de quota les porte aussi, avec Retry-After défini sur le nombre de secondes jusqu'à la réinitialisation.

curl -s -D - -o /dev/null https://api.gebai.ca/v2/models -H "Authorization: Bearer $GEB_API_KEY" | grep -i "x-quota\|x-ratelimit"

10.2 Limites de débit

LimiteValeur
Requêtes par minute (rafale), par compte120 pour tous les forfaits : Gratuit, Basic, Pro, Master et Developer. Comptées sur l'ensemble des clés d'un compte.
Requêtes simultanées, par compteUne part équitable de la capacité disponible à ce moment-là. Au-delà : 429 geb_concurrency_limit.

L'utilisation de l'API est mesurée par le nombre mensuel d'appels (section 10.1). La limite par minute n'existe que pour arrêter les rafales et les abus, elle est donc la même pour tous les forfaits. Elle est envoyée dans X-RateLimit-Limit. Une 429 de rafale porte Retry-After. Étalez les rafales, ou utilisez moins de requêtes en streaming, plus longues.

10.3 Limites des requêtes

LimiteValeur
Corps de requête32 MiB après décompression. gzip et deflate acceptés.
Messages et taille des messagesBornés par la fenêtre de contexte du modèle (context_window sur GET /v2/models). Un dépassement donne une 400 qui nomme la borne.
Images16 par requête, 20 MB chacune, 64 MB décodés au total.
Vos outils (tools)128 fonctions, 128 KiB sérialisés.
geb_tools32 noms.
Idempotency-Key255 caractères, conservée 24 heures.
Clés actives par compte10.
Conversations enregistréesAucune limite, conservées jusqu'à ce que vous les supprimiez.

10.4 Durée

Le streaming n'a pas de limite de durée totale tant que le modèle progresse. Un appel sans streaming est maintenu en vie au-delà du délai d'expiration en périphérie (section 6.6). Un appel qui ne progresse pas pendant trop longtemps se termine par une réponse partielle ou une 504 plutôt que de rester bloqué.

11.Compatibilité avec les SDK OpenAI

Les SDK Python et JavaScript d'OpenAI fonctionnent avec deux modifications : l'URL de base et la clé.

from openai import OpenAI
client = OpenAI(base_url="https://api.gebai.ca/v2", api_key="geb_live_...")
# Anything that reads the standard environment variables also works:
export OPENAI_BASE_URL="https://api.gebai.ca/v2"
export OPENAI_API_KEY="geb_live_..."

Ce qui fonctionne de la même façon : chat.completions.create (en réponse complète et en streaming), models.list et models.retrieve, l'appel de fonctions, response_format, stream_options.include_usage, les parties de contenu image, et les erreurs au format OpenAI avec Retry-After.

Différences à prévoir :

  • Uniquement des complétions de chat. Les appels aux embeddings, à l'audio, aux images, aux fichiers, aux traitements par lots ou à l'API Responses n'existent pas.
  • Les champs inconnus renvoient 400. Ne passez que les champs listés à la section 5.2. En particulier, utilisez max_tokens et non max_completion_tokens, et n'envoyez pas store ni metadata.
  • Les champs propres à Geb (geb_tools, geb_acceptance, conversation_id, locale) passent par extra_body en Python (le SDK le fusionne dans le corps de la requête). En JavaScript, passez-les comme propriétés supplémentaires.
  • Les champs d'échantillonnage sont ignorés. temperature, top_p, stop, seed et les pénalités sont acceptés et n'ont aucun effet.
  • Un seul modèle. model ne sélectionne rien. Utilisez client.models.list() pour voir ce qui est servi.
  • Niveaux de raisonnement. reasoning_effort accepte low, medium, high, et aussi ultra (envoyez-le via extra_body si la version de votre SDK type le champ).
  • Utilisation côté serveur. N'appelez pas l'API depuis un navigateur sur votre propre domaine.
  • Champs de réponse supplémentaires. Les réponses peuvent porter des champs propres à Geb tels que conversation_id et x_geb_*. Ils sont additifs.
  • Nouvelles tentatives. Les SDK réessaient d'eux-mêmes sur 429 et 5xx. Ajoutez une Idempotency-Key via extra_headers pour qu'un appel réessayé ne soit ni exécuté ni compté deux fois :
client.chat.completions.create(
    model="geb",
    messages=[{"role": "user", "content": "Hello"}],
    extra_headers={"Idempotency-Key": "order-7421-reply-1"},
)

12.Traitement des données

  • Aucune mémoire du compte. Les appels API ne lisent jamais la mémoire de votre compte Geb et n'y écrivent jamais. Cela vaut pour chaque clé et chaque forfait.
  • Les conversations enregistrées n'existent que si vous utilisez conversation_id. Elles sont séparées de la mémoire, lisibles uniquement par votre compte, et supprimées lorsque vous supprimez la conversation.
  • Journaux de l'API. Chaque appel est enregistré pour l'exploitation, le support et la prévention des abus : le contenu de la requête et de la réponse, le modèle, les paramètres, le nombre de jetons, la latence, le statut, votre agent utilisateur, l'identifiant de la clé et le pays de l'appelant. Les identifiants et secrets présents dans le contenu (clés, jetons, mots de passe, URL signées) sont masqués avant stockage. Les images en ligne sont stockées sous forme de taille et d'empreinte uniquement. Une copie de chaque appel est aussi visible par vous, et supprimable par vous, dans vos propres données de l'application Geb. Le journal d'exploitation est conservé pendant toute la vie du compte et supprimé lorsque le compte est supprimé. Les journaux de l'API ne sont jamais utilisés comme mémoire.
  • Les fichiers d'outils et les fichiers générés sont stockés sous votre compte et renvoyés sous forme de liens à durée limitée.

N'envoyez pas de données que vous n'avez pas le droit d'envoyer à un service tiers. Si vous avez besoin d'un accord de traitement des données, contactez Geb avant le lancement.

13.Version et assistance

Geb API v2. Servie sur https://api.gebai.ca/v2, avec /v1 conservé comme alias pour les clients existants. Révision 1.1 du guide, 28 octobre 2026.

Ce guide décrit le comportement de l'API en production à cette date. Les champs marqués comme ignorés pourront être pris en compte plus tard ; de nouveaux champs facultatifs peuvent apparaître dans les réponses. Traitez les champs de réponse inconnus comme additifs.

Lorsque vous avez besoin d'aide, indiquez le request_id du corps de l'erreur (ou l'en-tête x-request-id), l'heure de l'appel et la forme de la requête sans la clé. Contactez Geb via https://gebai.ca/contact.