OpenAI Compatibele Interface Upgrade: Diepe Ondersteuning voor Claude

31 jul 2026 · AIHubMix · 8 min read · Nieuws

OpenAI Compatibele Interface Upgrade: Diepe Ondersteuning voor Claude

We hebben de OpenAI-compatibele interface geüpgraded met diepere optimalisaties specifiek voor de Claude-serie modellen. Je kunt nu denken en caching preciezer en handiger controleren. Onderbroken denken in meerdaagse gesprekken is nu gebruiksvriendelijker, waardoor naadloze integratie mogelijk is zonder extra parameters. Het ondersteunt ook het inschakelen van de beta-functies die door Anthropic worden aangeboden.

1. Model Denken (Uitgebreid Denken)

1.1 Voordelen van Onderbroken Denken

Wanneer onderbroken denken niet is ingeschakeld, voert het model denken slechts één keer uit aan het begin van een assistent beurt; daaropvolgende reacties worden direct gegenereerd na het ontvangen van toolresultaten, zonder nieuwe denkblokken te produceren:

User → [Denken] → Tool Aanroep → Tool Resultaat → Reactie

Wanneer onderbroken denken is ingeschakeld, voegt het model elke keer dat het een toolresultaat ontvangt een nieuw denkblok toe, waardoor een keten van redenering ontstaat:

User → [Denken] → Tool Aanroep → Tool Resultaat → [Denken] → Reactie
                                                ↑ Onderbroken Denken

Dit stelt het model in staat om:

  • Secundaire redenering uit te voeren op basis van toolresultaten, in plaats van simpelweg outputs aan elkaar te plakken.
  • Redenering te koppelen tussen meerdere toolaanroepen, waarbij elke beslissing is gebaseerd op de analyse van de vorige stap.
Referentie: Anthropic Onderbroken Denken

1.2 Denken Inschakelen

Je kunt denken op vier manieren inschakelen, waarbij je er een van kunt kiezen:

Methode Voorbeeld Beschrijving
reasoning_effort "reasoning_effort": "low" OpenAI standaardparameter, geplaatst op het hoogste niveau van de aanvraagbody
reasoning.effort "reasoning": {"effort": "low"} Equivalent aan de vorige methode, geplaatst binnen het redeneringsobject
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Beheert nauwkeurig het maximale aantal tokens voor denken
Modelnaam met -think "model": "claude-sonnet-4-5-think" De eenvoudigste manier, vereist geen extra parameters
Prioriteit (wanneer meerdere methoden worden gebruikt): reasoning_effort > reasoning.max_tokens > reasoning.effort > -think suffix

Mogelijke waarden voor inspanning: minimal / low / medium / high / xhigh

1.3 Denken Teruggeven

Het antwoordbericht bevat twee nieuwe velden:

  • reasoning_content: Denkinhoud (string), voor gemakkelijke weergave.
  • reasoning_details: Volledige gestructureerde informatie over denken, die ongewijzigd moet worden teruggegeven in meerdaagse gesprekken; de interne structuur kan verschillen tussen aanbieders.

Niet-streaming voorbeeld (zonder niet-relevante velden):

{
  "choices": [{
    "message": {
      "role": "assistant",
      "content": "Hallo! Hoe kan ik je vandaag helpen?",
      "reasoning_content": "De gebruiker zegt gewoon hallo...",
      "reasoning_details": {
        "type": "denken",
        "thinking": "De gebruiker zegt gewoon hallo...",
        "signature": "Er8CCkYI..."
      }
    }
  }]
}

In streaming reacties zal de denkinhoud in stukken worden verzonden via delta.reasoning_content en delta.reasoning_details. Voor de volledige streaming concatenatielogica, zie het volledige voorbeeld hieronder.

1.4 Denken Behouden in Meerdaagse Gesprekken (Onderbroken Denken is ingebouwd, geen extra parameters nodig)

Om het model in staat te stellen zijn redeneervaardigheden voort te zetten in meerdaagse gesprekken, plaats je eenvoudig de eerder teruggegeven reasoning_details zoals het is in het volgende ronde assistentbericht:

messages = [
    {"role": "user", "content": "Hoe is het weer 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": "regenachtig"}',
    }
]

AIHubMix zal automatisch onderbroken denken inschakelen wanneer het historische denkinformatie in de aanvraag detecteert, waardoor het model diepere redenering kan voortzetten na het ontvangen van toolaanroepresultaten zonder extra parameters te vereisen.

1.5 Volledig Voorbeeld

De volgende twee voorbeelden demonstreren het volledige meerdaagse Tool Aanroep + onderbroken denkproces: gebruikersvraag → model denkt en roept een tool aan → injecteer toolresultaten (behoud reasoning_details) → model onderbroken denken geeft de uiteindelijke reactie.

Niet-streaming · Onderbroken Denken

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 definitie ───────────────────────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Haal het huidige weer op voor een locatie",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Stadsnaam"}},
            "required": ["location"]
        }
    }
}]

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

# ── Meerdaagse gesprekslus ─────────────────────────────
messages = [
    {"role": "user", "content": "Hoe is het weer in Boston? Aanbevelingen voor wat te dragen."}
]

turn = 0
while True:
    turn += 1
    print(f"\n── Beurt {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 denkproces
    if msg.reasoning_content:
        label = "Onderbroken Denken" if turn > 1 else "Denken"
        print(f"[{label}] {msg.reasoning_content}")

    # Print reactie-inhoud
    if msg.content:
        print(f"[Reactie] {msg.content}")

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

    # Bouw assistentbericht, behoud reasoning_details (kritisch!)
    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  # ongewijzigd teruggeven
    messages.append(assistant_msg)

    # Geen tool_calls betekent dat het gesprek is afgelopen
    if not msg.tool_calls:
        break

    # Voer tools uit en voeg resultaten toe aan berichten
    for tc in msg.tool_calls:
        args = json.loads(tc.function.arguments)
        result = execute_tool(tc.function.name, args)
        print(f"[Tool Resultaat: {tc.function.name}] {result}")
        messages.append({"role": "tool", "tool_call_id": tc.id, "content": result})

Streaming · Onderbroken Denken

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 definitie & mock uitvoering ─────────────────────────
tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "Haal het huidige weer op voor een locatie",
        "parameters": {
            "type": "object",
            "properties": {"location": {"type": "string", "description": "Stadsnaam"}},
            "required": ["location"]
        }
    }
}]

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

# ── Stream respons verzamelaar ────────────────────────────────
def stream_and_collect(turn: int, **kwargs):
    """Stream respons, print denken/inhoud in real-time, accumuleer reasoning_details/tool_calls."""
    rd = {}            # geaccumuleerde reasoning_details
    content = ""       # geaccumuleerde respons tekst
    tc_map = {}        # geaccumuleerde tool_calls (per index)
    cur = "none"       # huidige output sectie: none / denken / inhoud

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

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

        # ── Behandel inhoud ──
        if delta.content:
            if cur != "inhoud":
                if cur == "denken":
                    sys.stdout.write("\n")
                cur = "inhoud"
                sys.stdout.write("\n[Reactie] ")
            sys.stdout.write(delta.content)
            sys.stdout.flush()
            content += delta.content

        # ── Behandel 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 ""

    # Eind huidige output sectie
    if cur in ("denken", "inhoud"):
        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,
    }

# ── Meerdaagse gesprekslus ─────────────────────────────
messages = [
    {"role": "user", "content": "Hoe is het weer in Boston? Aanbevelingen voor wat te dragen."}
]

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

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

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

    # Bouw assistentbericht, behoud reasoning_details (kritisch!)
    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"]  # ongewijzigd teruggeven
    messages.append(assistant_msg)

    # Geen tool_calls betekent dat het gesprek is afgelopen
    if not result["tool_calls"]:
        break

    # Voer tools uit en voeg resultaten toe aan berichten
    for tc in result["tool_calls"]:
        args = json.loads(tc["function"]["arguments"])
        tool_result = execute_tool(tc["function"]["name"], args)
        print(f"[Tool Resultaat: {tc['function']['name']}] {tool_result}")
        messages.append({"role": "tool", "tool_call_id": tc["id"], "content": tool_result})

1.6 Regels voor Denken Intensiteit Mapping

Inspanning Modus:

  • Opus 4.6 / Sonnet 4.6 en hoger: map naar Anthropic's native Adaptief Denken inspanningsniveau.
  • Andere modellen: berekend met de formule voor budget_tokens:
budget_tokens = max(min(max_tokens × effort_ratio, 128000), 1024)
inspanning inspanning_ratio
xhigh 0.95
high 0.80
medium 0.50
low 0.20
minimal 0.10

Adaptieve Denken Inspanning Mapping:

Binnenkomende Inspanning Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

max_tokens Modus: Direct toegewezen als Anthropic's budget_tokens.

-think suffix: Opus/Sonnet 4.6+ gebruikt adaptief denken (inspanning=medium); andere modellen stellen budget_tokens = min(10240, max_tokens - 1), met een standaard max_tokens van 4096.


2. Prompt Caching

Je kunt Prompt Caching gebruiken bij het doen van aanvragen aan het Claude-model via de Chat-interface. Door cache_control breekpunten in berichten in te stellen, kunnen grote tekstblokken (zoals rolkaarten, RAG-gegevens, boekhoofdstukken, enz.) worden gecached voor hergebruik, waardoor daaropvolgende aanvragen direct de cache kunnen aanspreken en de kosten aanzienlijk kunnen verlagen.

Claude Officiële Documentatie: Prompt Caching

2.1 Caching Kosten

Operatie Prijsvermenigvuldiger (ten opzichte van originele invoerprijs)
Cache Schrijven (5-minuten TTL) 1.25x
Cache Schrijven (1-uur TTL) 2x
Cache Lezen 0.1x

2.2 Ondersteunde Modellen en Minimale Cache Lengte

Model Minimale Cache Token Aantal
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 (verouderd) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (verouderd) / Haiku 3 2048
Breakpoint Hoeveelheidslimiet: Een maximum van 4 cache_control breekpunten per aanvraag.

2.3 Cache TTL

TTL Syntax Toepasselijke Scenario's
5 minuten (standaard) "cache_control": {"type": "ephemeral"} Korte sessies, routine aanvragen
1 uur "cache_control": {"type": "ephemeral", "ttl": "1h"} Lange sessies, om herhaalde cache schrijfacties te vermijden

Schrijfkosten voor 1-uur TTL zijn hoger, maar ze kunnen totale uitgaven besparen door herhaalde schrijfacties in lange sessies te verminderen. Alle modellen vanaf Claude 4.5 en later van alle aanbieders (inclusief Anthropic, Amazon Bedrock, Google Vertex AI) ondersteunen 1-uur TTL.

2.4 Gebruik

Je kunt cache breekpunten instellen met behulp van het cache_control veld in system, user (inclusief afbeeldingen), en tools. De volgende voorbeelden tonen alleen de sleutelstructuur, waarbij grote tekstblokken zijn weggelaten.

Systeembericht Caching (standaard 5-minuten TTL):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [
        {"type": "text", "text": "Je bent een AI-assistent"},
        {
          "type": "text",
          "text": "(lange context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {
      "role": "user",
      "content": [{"type": "text", "text": "Hallo"}]
    }
  ]
}

Gebruikersbericht Caching (1-uur TTL):

{
  "model": "claude-opus-4-5",
  "messages": [
    {
      "role": "system",
      "content": [{"type": "text", "text": "Je bent een AI-assistent"}]
    },
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "(lange context)",
          "cache_control": {"type": "ephemeral", "ttl": "1h"}
        },
        {"type": "text", "text": "Hallo"}
      ]
    }
  ]
}

Afbeelding Bericht Caching:

{
  "role": "user",
  "content": [
    {
      "type": "image_url",
      "image_url": {"detail": "auto", "url": "data:image/jpeg;base64,/9j/4AAQ..."},
      "cache_control": {"type": "ephemeral"}
    },
    {"type": "text", "text": "Wat is dit?"}
  ]
}

Tool Definitie Caching:

cache_control wordt geplaatst op het hoogste niveau van het toolobject (naast type en function):

{
  "tools": [{
    "type": "function",
    "function": {
      "name": "get_weather",
      "description": "Haal het huidige weer op voor een locatie",
      "parameters": {
        "type": "object",
        "properties": {"city": {"type": "string"}},
        "required": ["city"]
      }
    },
    "cache_control": {"type": "ephemeral", "ttl": "1h"}
  }]
}

2.5 Cache Status Bekijken

De usage van de respons zal claude_cache_tokens_details retourneren, die gedetailleerde cache-informatie vastlegt:

Eerste Aanvraag (Cache Aanmaken):

{
  "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
    }
  }
}

Vervolg Aanvragen (Cache Hit):

{
  "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
    }
  }
}
Veld Betekenis
cache_creation_input_tokens Aantal tokens geschreven naar cache in deze aanvraag
cache_read_input_tokens Aantal tokens gelezen uit cache in deze aanvraag
cache_write_5_minutes_input_tokens Aantal tokens geschreven naar 5-minuten TTL cache
cache_write_1_hour_input_tokens Aantal tokens geschreven naar 1-uur TTL cache
prompt_tokens_details.cached_tokens Aantal gecachede tokens wanneer de cache wordt aangesproken, compatibel met OpenAI-formaat

3. Aanvraagheader voor anthropic-beta

Je kunt beta-functies van het Claude-model inschakelen via de HTTP-header anthropic-beta, die AIHubMix doorgeeft aan de Anthropic API.

Gebruik

Voeg anthropic-beta toe aan de aanvraagheader, met de waarde als de bijbehorende beta-functie-identificator:

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": "Je bent een AI-assistent"},
        {
          "type": "text",
          "text": "(lange context)",
          "cache_control": {"type": "ephemeral"}
        }
      ]
    },
    {"role": "user", "content": [{"type": "text", "text": "hallo"}]}
  ]
}'
Voor specifieke beschikbare beta-identificatoren, raadpleeg de Anthropic API Documentatie.

Laatste update: 2026-06-01

More from the blog