Aller au contenu
Résultats structurés

Résultats structurés

Demandez la réponse en JSON : un objet JSON quelconque, ou du JSON conforme à un schéma que vous envoyez. Utilisez-le quand c'est un programme, et non une personne, qui lit la réponse.

POST https://api.shannon-ai.com/v1/chat/completions

import json
from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY", base_url="https://api.shannon-ai.com/v1")

response = client.chat.completions.create(
    model="Kimi-K3-3BIT-REAP",
    messages=[
        {"role": "user", "content": "Extract the person: John Doe, 30 years old, engineer."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "person",
            "strict": True,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {"type": "string"},
                    "age": {"type": "integer"},
                    "occupation": {"type": "string"},
                },
                "required": ["name", "age", "occupation"],
            },
        },
    },
)

# The JSON arrives as text in message.content: parse it, then check it.
try:
    person = json.loads(response.choices[0].message.content)
except json.JSONDecodeError:
    person = None  # ask again, or report the failure

print(person)

Le JSON est le texte de la réponse, dans choices[0].message.content :

200 JSON
{
  "id": "chatcmpl-0c4e7a1b9d2f4e6a8b3c5d7e9f1a2b3c",
  "object": "chat.completion",
  "created": 1791590400,
  "model": "Kimi-K3-3BIT-REAP",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"engineer\"}",
        "reasoning_content": null
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 96,
    "completion_tokens": 19,
    "total_tokens": 115,
    "prompt_tokens_details": {"cached_tokens": 0},
    "completion_tokens_details": {"reasoning_tokens": 0}
  }
}

Les modes de response_format

response_format est un champ de /v1/chat/completions. Sans lui, la réponse est du texte ordinaire.

Mode Valeur Ce que vous obtenez
Objet JSON {"type": "json_object"} La réponse est du JSON sans forme imposée. Indiquez dans le prompt quelles clés vous voulez.
Schéma JSON {"type": "json_schema", "json_schema": {"name": "…", "schema": {…}}} La réponse suit le schéma que vous envoyez : ses clés, ses types et ses champs obligatoires.
Strict "strict": true Un champ de json_schema, pas un troisième mode. Il vaut true sauf si vous envoyez false.
Champ Type Par défaut Description Appliqué par
type string json_object ou json_schema. Obligatoire dans response_format. text est accepté et donne une réponse en texte ordinaire. Tout modèle qui accepte le mode
json_schema.schema object Le JSON Schema que la réponse suit. Envoyez-le chaque fois que type vaut json_schema. Tout modèle qui accepte le mode
json_schema.name string Un libellé pour le schéma, comme dans le format OpenAI. Modèles open-weight hébergés
json_schema.strict boolean true Indique si la réponse doit suivre exactement le schéma. Les modèles Shannon appliquent chaque schéma comme strict. Modèles open-weight hébergés

Le mode objet JSON n'a pas besoin de schéma. Nommez les clés dans le prompt :

Corps de la requête
{
  "model": "shannon-3",
  "messages": [
    {
      "role": "user",
      "content": "Name the capital of France. Answer as JSON with the keys city and country."
    }
  ],
  "response_format": {"type": "json_object"}
}

Prise en charge par modèle

Modèle json_object json_schema
Chaque modèle Shannon (shannon-*) Oui Oui
Modèles open-weight hébergés, sauf ceux nommés ci-dessous Oui Oui
Laguna-S-2.1-W4A16-AUTOROUND-REAP — —
inkling-W4A16-AUTOROUND-REAP Oui —

GET /v1/models indique la même chose par id : capabilities.json_schema sur chaque modèle, et capabilities.response_format sur les modèles open-weight hébergés. Modèles et tarifs

Mots-clés de schéma pris en charge

Ce qu'un modèle reçoit de votre schéma dépend du modèle et du point de terminaison :

Modèles /v1/chat/completions /v1/responses
Modèles Shannon Le sous-ensemble de mots-clés ci-dessous, appliqué comme strict. Le schéma tel que vous l'envoyez, appliqué comme strict.
Modèles open-weight hébergés Le schéma tel que vous l'envoyez. strict vaut true sauf si vous l'envoyez. Le schéma tel que vous l'envoyez. strict vaut true sauf si vous l'envoyez.

Le sous-ensemble de mots-clés des modèles Shannon sur /v1/chat/completions :

Mot-clé Comment c'est lu
Mots-clés lus type, format, title, description, nullable, enum, items, properties, required, minItems, maxItems, minimum, maximum, minLength, maxLength, pattern, anyOf, default
$ref Résolu depuis $defs ou definitions au niveau supérieur du schéma. Les mots-clés écrits à côté du $ref, comme description, sont conservés. Une référence sans définition correspondante est lue comme {"type": "object"}.
allOf Les membres sont fusionnés en un seul schéma. Quand deux membres définissent le même mot-clé, le dernier l'emporte.
oneOf Lu comme anyOf.
Schémas qui se référencent eux-mêmes Développés sur 24 niveaux. Un niveau plus profond est lu comme {"type": "object"}.
Hors du sous-ensemble Tout autre mot-clé, par exemple additionalProperties, const, multipleOf, uniqueItems, exclusiveMinimum, exclusiveMaximum, patternProperties, prefixItems, minProperties, maxProperties, not et if / then / else. Ils ne font pas partie du schéma que le modèle reçoit.

Sur /v1/responses : text.format

/v1/responses écrit la même requête sous la forme text.format. Le schéma se trouve directement dans format, à côté de name et strict, et non sous une clé json_schema. Pour le mode objet JSON, envoyez {"format": {"type": "json_object"}}.

Corps de la requête
{
  "model": "Kimi-K3-3BIT-REAP",
  "input": "Extract the person: John Doe, 30 years old, engineer.",
  "text": {
    "format": {
      "type": "json_schema",
      "name": "person",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "age": {"type": "integer"},
          "occupation": {"type": "string"}
        },
        "required": ["name", "age", "occupation"]
      }
    }
  }
}

Le JSON est le text de la partie output_text de l'élément message :

200 JSON
{
  "output": [
    {
      "id": "msg_7e1d3c5b9a2f4d6e8c0b1a3f5e7d9c2b",
      "type": "message",
      "role": "assistant",
      "status": "completed",
      "content": [
        {
          "type": "output_text",
          "text": "{\"name\":\"John Doe\",\"age\":30,\"occupation\":\"engineer\"}",
          "annotations": []
        }
      ]
    }
  ]
}

La prise en charge par modèle et les erreurs sont les mêmes que sur /v1/chat/completions. API Responses

Sur /v1/messages : un outil comme schéma

Le format Messages n'a pas de champ de sortie structurée. Pour obtenir du JSON des mêmes modèles, appelez /v1/chat/completions ou /v1/responses avec la même clé.

Pour rester sur /v1/messages, décrivez l'objet comme un outil : placez votre schéma dans input_schema et nommez l'outil dans tool_choice. L'input du bloc tool_use est alors votre objet, déjà analysé. Un tool_choice nommé est appliqué par les modèles open-weight hébergés.

Corps de la requête
{
  "model": "Kimi-K3-3BIT-REAP",
  "max_tokens": 1024,
  "messages": [
    {
      "role": "user",
      "content": "Extract the person: John Doe, 30 years old, engineer."
    }
  ],
  "tools": [
    {
      "name": "record_person",
      "description": "Record the extracted person.",
      "input_schema": {
        "type": "object",
        "properties": {
          "name": {"type": "string"},
          "age": {"type": "integer"},
          "occupation": {"type": "string"}
        },
        "required": ["name", "age", "occupation"]
      }
    }
  ],
  "tool_choice": {"type": "tool", "name": "record_person"}
}
200 JSON
{
  "stop_reason": "tool_use",
  "content": [
    {
      "type": "tool_use",
      "id": "toolu_4b8d2f6a1c3e4a5b9d7f0e2c4a6b8d1f",
      "name": "record_person",
      "input": {"name": "John Doe", "age": 30, "occupation": "engineer"}
    }
  ]
}

Vérifiez que stop_reason vaut tool_use avant de lire le bloc. Appel de fonction

Comment le JSON arrive

  • Le JSON est une chaîne. La réponse n'a pas d'objet déjà analysé à côté : lancez votre parseur JSON sur message.content.
  • Avec stream: true, le JSON arrive en morceaux dans delta.content. Assemblez-les et analysez une fois le flux terminé ; un morceau seul n'est pas du JSON valide.
  • Un modèle open-weight hébergé répond à une requête response_format sans texte de raisonnement : reasoning_content vaut null.

Vérifiez ce que vous recevez

Le schéma guide la façon dont le modèle écrit. L'API ne compare pas la réponse terminée à votre schéma, donc c'est votre code qui fait la vérification finale :

  • Analysez dans un gestionnaire d'erreurs, comme le fait l'exemple en haut.
  • Validez l'objet analysé avec le même schéma dans votre propre code, par exemple avec une bibliothèque JSON Schema, Pydantic ou Zod.
  • Une longue réponse peut se terminer avant que le JSON soit complet. Une réponse sans streaming indique seulement stop ou tool_calls comme finish_reason, donc une erreur d'analyse est le signal. Redemandez, ou demandez un objet plus petit.

Erreurs

Statut Type Message Quand
400 invalid_request_error <id> does not accept response_format Le modèle n'accepte aucun des deux modes.
400 invalid_request_error <id> does not accept response_format json_schema; use json_object Le modèle accepte json_object et vous avez envoyé json_schema.
422 invalid_request_error Failed to deserialize the JSON body into the target type: … response_format n'a pas de type, ou n'est pas un objet JSON.

Les deux réponses 400 sont envoyées avant que quoi que ce soit ne soit prélevé sur votre solde. Gestion des erreurs