Mise à niveau de l'interface compatible OpenAI : prise en charge approfondie de Claude

AIHubMix a amélioré son API compatible OpenAI pour Claude : réflexion entrelacée sans paramètres supplémentaires, mise en cache des prompts et prise en charge des fonctionnalités bêta d'Anthropic.

AIHubMix8 min de lecture
Mise à niveau de l'interface compatible OpenAI : prise en charge approfondie de Claude

Nous avons amélioré l'interface compatible OpenAI avec des optimisations plus poussées, spécifiquement pour les modèles de la série Claude. Vous pouvez désormais contrôler la réflexion et la mise en cache de manière plus précise et plus pratique. La réflexion entrelacée dans les conversations multi-tours est maintenant plus conviviale : elle s'intègre de façon transparente, sans paramètres supplémentaires. Elle permet aussi d'activer les fonctionnalités bêta proposées par Anthropic.

1. Réflexion du modèle (Extended Thinking)

1.1 Avantages de la réflexion entrelacée

Lorsque la réflexion entrelacée n'est pas activée, le modèle ne réfléchit qu'une seule fois, au début d'un tour de l'assistant ; les réponses suivantes sont générées directement après la réception des résultats d'outils, sans produire de nouveaux blocs de réflexion :

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

Lorsque la réflexion entrelacée est activée, le modèle insère un nouveau bloc de réflexion à chaque réception d'un résultat d'outil, formant une chaîne de raisonnement :

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

Cela permet au modèle :

  • d'effectuer un raisonnement secondaire à partir des résultats d'outils, au lieu de simplement concaténer les sorties.
  • d'enchaîner le raisonnement entre plusieurs appels d'outils, chaque décision reposant sur l'analyse de l'étape précédente.
Référence : Anthropic Interleaved Thinking

1.2 Activer la réflexion

Vous pouvez activer la réflexion de quatre manières, en choisissant celle de votre choix :

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 reasoning
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Contrôle précisément le nombre maximal de tokens pour la réflexion
Nom du modèle avec le suffixe -think "model": "claude-sonnet-4-5-think" La façon la plus simple, sans paramètres supplémentaires
Priorité (lorsque plusieurs méthodes sont utilisées) : reasoning_effort > reasoning.max_tokens > reasoning.effort > suffixe -think

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

1.3 Retour de la réflexion

Le message de réponse comprend deux nouveaux champs :

  • reasoning_content : contenu de la réflexion (chaîne de caractères), pour un affichage facile.
  • reasoning_details : informations structurées complètes sur la réflexion, qui doivent être renvoyées telles quelles dans les conversations multi-tours ; la structure interne peut varier selon le fournisseur.

Exemple de réponse non diffusée en continu (champs non pertinents omis) :

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Hello! How can I help you today?",
      "reasoning_content": "The user is just saying hello...",
      "reasoning_details": {
        "type": "thinking",
        "thinking": "The user is just saying hello...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

Dans les réponses en streaming, le contenu de la réflexion est envoyé par fragments via delta.reasoning_content et delta.reasoning_details. Pour la logique complète de concaténation en streaming, reportez-vous à l'exemple complet ci-dessous.

1.4 Conserver la réflexion dans les conversations multi-tours (la réflexion entrelacée est intégrée, aucun paramètre supplémentaire n'est nécessaire)

Pour permettre au modèle de poursuivre ses capacités de raisonnement dans les conversations multi-tours, placez simplement les reasoning_details renvoyés précédemment tels quels dans le message de l'assistant du tour suivant :

messages = [
    {"role": "user", "content": "What's the weather like in 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": "rainy"}',
    }
]

AIHubMix active automatiquement la réflexion entrelacée lorsqu'il détecte des informations de réflexion historiques dans la requête, ce qui permet au modèle de poursuivre un raisonnement approfondi après réception des résultats d'appels d'outils, sans paramètres supplémentaires.

1.5 Exemple complet

Les deux exemples suivants illustrent le processus complet d'appel d'outils multi-tours avec réflexion entrelacée : question de l'utilisateur → le modèle réfléchit et appelle un outil → injection des résultats de l'outil (en conservant reasoning_details) → le modèle réfléchit de manière entrelacée et donne la réponse finale.

Sans streaming · Réflexion 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-***"),
)

# ── Tool definition ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "City name"}},
            "required": ["location"]
        }
    }
}]

# ── Mock tool execution ───────────────────────────────────────
WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "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": "clear"}))
    return "{}"

# ── Multi-turn conversation loop ─────────────────────────────
messages = [
    {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Turn {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

    # Print thinking process
    if msg.reasoning_content:
        label = "Interleaved Thinking" if turn > 1 else "Thinking"
        print(f"[{label}] {msg.reasoning_content}")

    # Print response content
    if msg.content:
        print(f"[Response] {msg.content}")

    # Print tool calls
    if msg.tool_calls:
        for tc in msg.tool_calls:
            print(f"[Tool Call: {tc.function.name}] {tc.function.arguments}")

    # Build assistant message, preserve reasoning_details (critical!)
    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  # pass back unmodified
    messages.append(assistant_msg)

    # No tool_calls means conversation is done
    if not msg.tool_calls:
        break

    # Execute tools and append results to messages
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Tool Result: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Streaming · Réflexion 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-***"),
)

# ── Tool definition & mock execution ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Get current weather for a location",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "City name"}},
            "required": ["location"]
        }
    }
}]

WEATHER_DB = {
    "boston": {"temperature": "45°F (7°C)", "condition": "rainy", "humidity": "85%", "wind": "15 mph NE"},
    "tokyo":  {"temperature": "72°F (22°C)", "condition": "sunny", "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": "clear"}))
    return "{}"

# ── Stream response collector ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Stream response, print thinking/content in real-time, accumulate reasoning_details/tool_calls."""
    rd = {}            # accumulated reasoning_details
    content = ""       # accumulated response text
    tc_map = {}        # accumulated tool_calls (by index)
    cur = "none"       # current output section: 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

        # ── Handle thinking ──
        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
            # Print thinking chunks in real-time
            thinking_chunk = rd_delta.get("thinking", "")
            if thinking_chunk:
                if cur != "thinking":
                    cur = "thinking"
                    label = "Interleaved Thinking" if turn > 1 else "Thinking"
                    sys.stdout.write(f"\n[{label}] ")
                sys.stdout.write(thinking_chunk)
                sys.stdout.flush()

        # ── Handle content ──
        if delta.content:
            if cur != "content":
                if cur == "thinking":
                    sys.stdout.write("\n")
                cur = "content"
                sys.stdout.write("\n[Response] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Handle tool_calls ──
        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 ""

    # End current output section
    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,
    }

# ── Multi-turn conversation loop ─────────────────────────────
messages = [
    {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
]

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

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

    # Print tool calls
    if result["tool_calls"]:
        for tc in result["tool_calls"]:
            print(f"[Tool Call: {tc['function']['name']}] {tc['function']['arguments']}")

    # Build assistant message, preserve reasoning_details (critical!)
    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"]  # pass back unmodified
    messages.append(assistant_msg)

    # No tool_calls means conversation is done
    if not result["tool_calls"]:
        break

    # Execute tools and append results to messages
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Tool Result: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Règles de correspondance de l'intensité de réflexion

Mode effort :

  • Opus 4.6 / Sonnet 4.6 et supérieurs : correspond au niveau d'effort natif Adaptive Thinking d'Anthropic.
  • Autres modèles : calculé à l'aide de la formule de 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

Correspondance de l'effort en Adaptive Thinking :

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 : directement attribué comme budget_tokens d'Anthropic.

-think suffixe : Opus/Sonnet 4.6+ utilise la réflexion adaptative (effort=medium) ; pour les autres modèles, budget_tokens = min(10240, max_tokens - 1), avec une valeur par défaut de max_tokens de 4096.


2. Mise en cache des prompts (Prompt Caching)

Vous pouvez utiliser la mise en cache des prompts lorsque vous envoyez des requêtes au modèle Claude via l'interface Chat. En définissant des points d'arrêt cache_control dans les messages, de grands blocs de texte (comme des fiches de rôle, des données RAG, des chapitres de livres, etc.) peuvent être mis en cache pour être réutilisés. Les requêtes suivantes atteignent alors directement le cache, ce qui réduit considérablement les coûts.

Documentation officielle de Claude : Prompt Caching

2.1 Coûts de mise en cache

Opération Multiplicateur de prix (par rapport au prix d'entrée d'origine)
Écriture en cache (TTL de 5 minutes) 1.25x
Écriture en cache (TTL d'1 heure) 2x
Lecture du cache 0.1x

2.2 Modèles pris en charge et longueur minimale de cache

Modèle Nombre minimal de tokens à mettre en 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 (obsolète) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (obsolète) / Haiku 3 2048
Limite du nombre de points d'arrêt : un maximum de 4 points d'arrêt cache_control par requête.

2.3 TTL du cache

TTL Syntaxe Scénarios d'utilisation
5 minutes (par défaut) "cache_control": {"type": "ephemeral"} Sessions courtes, requêtes courantes
1 heure "cache_control": {"type": "ephemeral", "ttl": "1h"} Sessions longues, pour éviter des écritures répétées dans le cache

Les coûts d'écriture pour un TTL d'1 heure sont plus élevés, mais ils peuvent réduire la dépense totale en limitant les écritures répétées lors de longues sessions. Tous les modèles Claude à partir de la version 4.5, chez tous les fournisseurs (y compris Anthropic, Amazon Bedrock et Google Vertex AI), prennent en charge un TTL d'1 heure.

2.4 Utilisation

Vous pouvez définir des points d'arrêt de cache à l'aide du champ cache_control dans system, user (y compris les images) et tools. Les exemples suivants montrent uniquement la structure clé, sans les grands blocs de texte.

Mise en cache du message système (TTL de 5 minutes par défaut) :

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "You are an AI assistant"},
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Hello"}]
    }
  ]
}

Mise en cache du message utilisateur (TTL d'1 heure) :

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "You are an AI assistant"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Hello"}
      ]
    }
  ]
}

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": "What's this?"}
  ]
}

Mise en cache des définitions d'outils :

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

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Get current weather for a location",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Consulter l'état du cache

La valeur usage de la réponse renvoie claude_cache_tokens_details, qui enregistre les 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 suivantes (succès du 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 depuis le cache lors de cette requête
cache_write_5_minutes_input_tokens Nombre de tokens écrits dans le cache avec un TTL de 5 minutes
cache_write_1_hour_input_tokens Nombre de tokens écrits dans le cache avec un TTL d'1 heure
prompt_tokens_details.cached_tokens Nombre de tokens mis en cache en cas de succès du 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, qu'AIHubMix transmet à l'API d'Anthropic.

Utilisation

Ajoutez anthropic-beta à l'en-tête de la requête, avec pour valeur 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": "You are an AI assistant"},
        {
          "type": "text",
          "text": "(long context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "hello"}]}
  ]
}'
Pour connaître les identifiants bêta disponibles, reportez-vous à la documentation de l'API Anthropic.

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

Une seule passerelle vers plusieurs fournisseurs d’IA. Conçue pour la production.

Découvrir AIHubMix