Mise à niveau de l'interface compatible OpenAI : support approfondi pour Claude

31 juil. 2026 · AIHubMix · 8 min read · Actualités

Mise à niveau de l'interface compatible OpenAI : support approfondi pour Claude

Nous avons mis à niveau l'interface compatible avec OpenAI avec des optimisations plus profondes spécifiquement pour les modèles de la série Claude. Vous pouvez désormais contrôler la pensée et la mise en cache de manière plus précise et pratique. La pensée entrelacée dans les conversations à plusieurs tours est désormais plus conviviale, permettant une intégration transparente sans paramètres supplémentaires. Elle prend également en charge l'activation des fonctionnalités bêta proposées par Anthropic.

1. Pensée du Modèle (Pensée Étendue)

1.1 Avantages de la Pensée Entrelacée

Lorsque la pensée entrelacée n'est pas activée, le modèle effectue une pensée uniquement une fois au début d'un tour d'assistant ; les réponses suivantes sont générées directement après avoir reçu les résultats des outils, sans produire de nouveaux blocs de pensée :

User → [Thinking] → Tool Call → Tool Result → Response

Lorsque la pensée entrelacée est activée, le modèle insère un nouveau bloc de pensée chaque fois qu'il reçoit un résultat d'outil, formant une chaîne de raisonnement :

User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
                                                ↑ Pensée Entrelacée

Cela permet au modèle de :

  • Effectuer un raisonnement secondaire basé sur les résultats des outils, plutôt que de simplement concaténer les sorties.
  • Chaîner le raisonnement entre plusieurs appels d'outils, où chaque décision est basée sur l'analyse de l'étape précédente.
Référence : Pensée Entrelacée d'Anthropic

1.2 Activation de la Pensée

Vous pouvez activer la pensée de quatre manières, en choisissant l'une d'elles :

Méthode Exemple Description
reasoning_effort "reasoning_effort": "low" Paramètre standard d'OpenAI, placé au niveau supérieur du corps de la requête
reasoning.effort "reasoning": {"effort": "low"} Équivalent à la méthode précédente, placé dans l'objet de raisonnement
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Contrôle précisément le nombre maximum de tokens pour la pensée
Nom du modèle avec -think "model": "claude-sonnet-4-5-think" La méthode la plus simple, ne nécessite pas de paramètres supplémentaires
Priorité (lorsque plusieurs méthodes sont utilisées) : reasoning_effort > reasoning.max_tokens > reasoning.effort > -think suffixe

Valeurs possibles pour l'effort : minimal / low / medium / high / xhigh

1.3 Retour de Pensée

Le message de réponse inclura deux nouveaux champs :

  • reasoning_content : Contenu de la pensée (chaîne), pour un affichage facile.
  • reasoning_details : Informations structurées complètes sur la pensée, qui doivent être retournées telles quelles dans les conversations à plusieurs tours ; la structure interne peut différer entre les fournisseurs.

Exemple non en streaming (omettant les champs non pertinents) :

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Bonjour ! Comment puis-je vous aider aujourd'hui ?",
      "reasoning_content": "L'utilisateur dit juste bonjour...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "L'utilisateur dit juste bonjour...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

Dans les réponses en streaming, le contenu de la pensée sera envoyé par morceaux via delta.reasoning_content et delta.reasoning_details. Pour la logique complète de concaténation en streaming, référez-vous à l'exemple complet ci-dessous.

1.4 Conservation de la Pensée dans les Conversations à Plusieurs Tours (La Pensée Entrelacée est intégrée, aucun paramètre supplémentaire n'est nécessaire)

Pour permettre au modèle de continuer ses capacités de raisonnement dans les conversations à plusieurs tours, il suffit de placer les reasoning_details retournés précédemment tels quels dans le message de l'assistant du tour suivant :

messages = [
    {"role": "user", "content": "Quel temps fait-il à Boston ?"},
    {
        "role": "assistant",
        "content": response.choices[0].message.content,
        "tool_calls": response.choices[0].message.tool_calls,
        "reasoning_details": response.choices[0].message.reasoning_details,
    },
    {
        "role": "tool",
        "tool_call_id": "toolu_xxx",
        "content": '{"temperature": 45, "condition": "pluvieux"}',
    }
]

AIHubMix activera automatiquement la pensée entrelacée lorsqu'il détecte des informations de pensée historiques dans la requête, permettant au modèle de continuer un raisonnement approfondi après avoir reçu les résultats des appels d'outils sans nécessiter de paramètres supplémentaires.

1.5 Exemple Complet

Les deux exemples suivants démontrent le processus complet d'appel d'outil à plusieurs tours + pensée entrelacée : demande de l'utilisateur → le modèle pense et appelle un outil → injecte les résultats de l'outil (préservant reasoning_details) → le modèle la pensée entrelacée donne la réponse finale.

Non-streaming · Pensée Entrelacée

import os
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Définition de l'outil ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtenir la météo actuelle pour un emplacement",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nom de la ville"}},
            "required": ["location"]
        }
    }
}]

# ── Exécution simulée de l'outil ─────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "pluvieux", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "ensoleillé", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clair"}))
    return "{}"

# ── Boucle de conversation à plusieurs tours ───────────────────────────
messages = [
    {"role": "user", "content": "Quel temps fait-il à Boston ? Puis recommandez quoi porter."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Tour {turn} ──")

    response = client.chat.completions.create(
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )
    msg = response.choices[0].message

    # Imprimer le processus de pensée
    if msg.reasoning_content:
        label = "Pensée Entrelacée" if turn > 1 else "Pensée"
        print(f"[{label}] {msg.reasoning_content}")

    # Imprimer le contenu de la réponse
    if msg.content:
        print(f"[Réponse] {msg.content}")

    # Imprimer les appels d'outils
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Appel d'Outil : {tc.function.name}] {tc.function.arguments}")

    # Construire le message de l'assistant, préserver reasoning_details (critique !)
    assistant_msg = {"role": "assistant", "content": msg.content}
    if msg.tool_calls:
        assistant_msg["tool_calls"] = msg.tool_calls
    if msg.reasoning_details:
        assistant_msg["reasoning_details"] = msg.reasoning_details  # passer sans modification
    messages.append(assistant_msg)

    # Pas d'appels d'outils signifie que la conversation est terminée
    if not msg.tool_calls:
        break

    # Exécuter les outils et ajouter les résultats aux messages
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Résultat de l'Outil : {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Streaming · Pensée Entrelacée

import os
import sys
import json
from openai import OpenAI

client = OpenAI(
    base_url="https://aihubmix.com/v1",
    api_key=os.environ.get("AIHUBMIX_API_KEY", "sk-***"),
)

# ── Définition de l'outil & exécution simulée ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Obtenir la météo actuelle pour un emplacement",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Nom de la ville"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "pluvieux", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "ensoleillé", "humidity": "45%", "wind": "5 mph S"},
}

def execute_tool(name: str, args: dict) -> str:
    if name == "get_weather":
        key = next((k for k in WEATHER_DB if k in args.get("location", "").lower()), None)
        return json.dumps(WEATHER_DB.get(key, {"temperature": "65°F", "condition": "clair"}))
    return "{}"

# ── Collecteur de réponse en streaming ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Streamer la réponse, imprimer la pensée/contenu en temps réel, accumuler reasoning_details/tool_calls."""
    rd = {}            # détails de raisonnement accumulés
    content = ""       # texte de réponse accumulé
    tc_map = {}        # appels d'outils accumulés (par index)
    cur = "none"       # section de sortie actuelle : none / thinking / content

    stream = client.chat.completions.create(stream=True, **kwargs)
    for chunk in stream:
        if not chunk.choices:
            continue
        delta = chunk.choices[0].delta

        # ── Gérer la pensée ──
        rd_delta = getattr(delta, "reasoning_details", None)
        if rd_delta and isinstance(rd_delta, dict):
            for k, v in rd_delta.items():
                if k == "type":
                    rd[k] = v
                elif isinstance(v, str):
                    rd[k] = rd.get(k, "") + v
                elif v is not None:
                    rd[k] = v
            # Imprimer les morceaux de pensée en temps réel
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Pensée Entrelacée" if turn > 1 else "Pensée"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Gérer le contenu ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Réponse] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Gérer les appels d'outils ──
        for tc in delta.tool_calls or []:
            i = tc.index
            if i not in tc_map:
                tc_map[i] = {"id": "", "type": "function",
                             "function": {"name": "", "arguments": ""}}
            if tc.id:
                tc_map[i]["id"] = tc.id
            if tc.function:
                tc_map[i]["function"]["name"] += tc.function.name or ""
                tc_map[i]["function"]["arguments"] += tc.function.arguments or ""

    # Fin de la section de sortie actuelle
    if cur in ("thinking", "content"):
        sys.stdout.write("\n")

    tool_calls = [tc_map[i] for i in sorted(tc_map)] if tc_map else None
    return {
        "content": content or None,
        "reasoning_details": rd or None,
        "tool_calls": tool_calls,
    }

# ── Boucle de conversation à plusieurs tours ───────────────────────────
messages = [
    {"role": "user", "content": "Quel temps fait-il à Boston ? Puis recommandez quoi porter."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Tour {turn} ──")

    result = stream_and_collect(
        turn,
        model="claude-sonnet-4-5",
        messages=messages,
        tools=tools,
        extra_body={"reasoning": {"max_tokens": 2000}},
    )

    # Imprimer les appels d'outils
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Appel d'Outil : {tc['function']['name']}] {tc['function']['arguments']}")

    # Construire le message de l'assistant, préserver reasoning_details (critique !)
    assistant_msg = {"role": "assistant", "content": result["content"]}
    if result["tool_calls"]:
        assistant_msg["tool_calls"] = result["tool_calls"]
    if result["reasoning_details"]:
        assistant_msg["reasoning_details"] = result["reasoning_details"]  # passer sans modification
    messages.append(assistant_msg)

    # Pas d'appels d'outils signifie que la conversation est terminée
    if not result["tool_calls"]:
        break

    # Exécuter les outils et ajouter les résultats aux messages
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Résultat de l'Outil : {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Règles de Cartographie de l'Intensité de Pensée

Mode Effort :

  • Opus 4.6 / Sonnet 4.6 et supérieur : correspond au niveau d'effort Pensée Adaptative natif d'Anthropic.
  • Autres modèles : calculé en utilisant la formule pour budget_tokens :
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
effort effort_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Cartographie de l'Effort de Pensée Adaptative :

Effort Entrant Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

Mode max_tokens : Assigné directement comme budget_tokens d'Anthropic.

-think suffixe : Opus/Sonnet 4.6+ utilise la pensée adaptative (effort=medium) ; d'autres modèles définissent budget_tokens = min(10240, max_tokens - 1), avec un max_tokens par défaut de 4096.


2. Mise en Cache des Invites

Vous pouvez utiliser la mise en cache des invites lors de l'envoi de requêtes au modèle Claude via l'interface de chat. En définissant des points de rupture cache_control dans les messages, de grands blocs de texte (comme des cartes de rôle, des données RAG, des chapitres de livres, etc.) peuvent être mis en cache pour réutilisation, permettant aux requêtes suivantes d'accéder directement au cache et de réduire considérablement les coûts.

Documentation Officielle de Claude : Mise en Cache des Invites

2.1 Coûts de Mise en Cache

Opération Multiplicateur de Prix (par rapport au prix d'entrée original)
Écriture de Cache (TTL de 5 minutes) 1.25x
Écriture de Cache (TTL de 1 heure) 2x
Lecture de Cache 0.1x

2.2 Modèles Supportés et Longueur Minimale de Cache

Modèle Nombre Minimum de Tokens de Cache
Claude Opus 4.8 1024
Claude Opus 4.7 2048
Claude Opus 4.6 / Opus 4.5 4096
Claude Sonnet 4.6 / Sonnet 4.5 / Opus 4.1 / Opus 4 / Sonnet 4 / Sonnet 3.7 (déprécié) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (déprécié) / Haiku 3 2048
Limite de Quantité de Points de Rupture : Un maximum de 4 points de rupture cache_control par requête.

2.3 TTL de Cache

TTL Syntaxe Scénarios Applicables
5 minutes (par défaut) "cache_control": {"type": "ephemeral"} Sessions courtes, requêtes de routine
1 heure "cache_control": {"type": "ephemeral", "ttl": "1h"} Longues sessions, pour éviter les écritures de cache répétées

Les coûts d'écriture pour un TTL de 1 heure sont plus élevés, mais ils peuvent économiser des dépenses totales en réduisant les écritures répétées dans les longues sessions. Tous les modèles à partir de Claude 4.5 et plus de tous les fournisseurs (y compris Anthropic, Amazon Bedrock, Google Vertex AI) prennent en charge un TTL de 1 heure.

2.4 Utilisation

Vous pouvez définir des points de rupture de cache en utilisant le champ cache_control dans system, user (y compris les images), et tools. Les exemples suivants montrent uniquement la structure clé, omettant de grands blocs de texte.

Mise en Cache des Messages Système (TTL par défaut de 5 minutes) :

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Vous êtes un assistant IA"},
        {
          "type": "text",
          "text": "(long contexte)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Bonjour"}]
    }
  ]
}

Mise en Cache des Messages Utilisateurs (TTL de 1 heure) :

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Vous êtes un assistant IA"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(long contexte)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Bonjour"}
      ]
    }
  ]
}

Mise en Cache des Messages d'Image :

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "Qu'est-ce que c'est ?"}
  ]
}

Mise en Cache de la Définition de l'Outil :

cache_control est placé au niveau supérieur de l'objet outil (aux côtés de type et function) :

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Obtenir la météo actuelle pour un emplacement",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Visualisation de l'État du Cache

Le champ usage de la réponse retournera claude_cache_tokens_details, enregistrant des informations détaillées sur le cache :

Première Requête (Création du Cache) :

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 890,
    "total_tokens": 912,
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 6266,
      "cache_read_input_tokens": 0,
      "cache_write_5_minutes_input_tokens": 6266,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}

Requêtes Subséquentes (Accès au Cache) :

{
  "usage": {
    "prompt_tokens": 22,
    "completion_tokens": 810,
    "total_tokens": 832,
    "prompt_tokens_details": {
      "cached_tokens": 6266
    },
    "claude_cache_tokens_details": {
      "cache_creation_input_tokens": 0,
      "cache_read_input_tokens": 6266,
      "cache_write_5_minutes_input_tokens": 0,
      "cache_write_1_hour_input_tokens": 0
    }
  }
}
Champ Signification
cache_creation_input_tokens Nombre de tokens écrits dans le cache lors de cette requête
cache_read_input_tokens Nombre de tokens lus dans le cache lors de cette requête
cache_write_5_minutes_input_tokens Nombre de tokens écrits dans le cache TTL de 5 minutes
cache_write_1_hour_input_tokens Nombre de tokens écrits dans le cache TTL de 1 heure
prompt_tokens_details.cached_tokens Nombre de tokens mis en cache lors de l'accès au cache, compatible avec le format OpenAI

3. En-tête de Requête pour anthropic-beta

Vous pouvez activer les fonctionnalités bêta du modèle Claude via l'en-tête HTTP anthropic-beta, que AIHubMix transmettra à l'API Anthropic.

Utilisation

Ajoutez anthropic-beta à l'en-tête de la requête, avec la valeur étant l'identifiant de la fonctionnalité bêta correspondante :

curl "https://aihubmix.com/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $AIHUBMIX_API_KEY" \
  -H "anthropic-beta: context-1m-2025-08-07" \
  -d '{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Vous êtes un assistant IA"},
        {
          "type": "text",
          "text": "(long contexte)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "bonjour"}]}
  ]
}'
Pour des identifiants bêta spécifiques disponibles, veuillez vous référer à la Documentation de l'API Anthropic.

Dernière mise à jour : 2026-06-01

More from the blog