> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aihubmix.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Adaptación profunda de la interfaz compatible con OpenAI para Claude

> AIHubMix mejora la interfaz compatible con OpenAI con una adaptación profunda para Claude: razonamiento intercalado multiturno sin parámetros adicionales, acierto automático de caché de prompts y soporte para activar las funciones beta de Anthropic, aprovechando al máximo las capacidades nativas de Claude bajo el protocolo de OpenAI.

<Frame>
  <img src="https://mintcdn.com/aihubmix/KfVPdfHEI_4FVLQw/images/blogs/aihubmix-openai-upgrade-claude.webp?fit=max&auto=format&n=KfVPdfHEI_4FVLQw&q=85&s=fb6ff92d4f19b7f29976a709ab43ba76" alt="Adaptación profunda de la interfaz compatible con OpenAI de AIHubMix para el razonamiento, la caché y las funciones beta de Claude" width="2400" height="1260" data-path="images/blogs/aihubmix-openai-upgrade-claude.webp" />
</Frame>

Hemos mejorado la interfaz compatible con OpenAI con optimizaciones más profundas específicamente para los modelos de la serie Claude. Ahora puedes controlar el razonamiento y el caching con mayor precisión y comodidad, especialmente el razonamiento intercalado (interleaved thinking) en conversaciones multiturno, que hemos hecho más fácil de usar y permite una integración sin fricciones sin parámetros adicionales. También admite la activación de las funciones beta ofrecidas por Anthropic.

## 1. Razonamiento del modelo (Extended Thinking)

### 1.1 Ventajas del razonamiento intercalado

Cuando el razonamiento intercalado no está habilitado, el modelo realiza el razonamiento solo una vez al inicio del turno del asistente; las respuestas posteriores se generan directamente tras recibir los resultados de las herramientas, sin producir nuevos bloques de razonamiento:

```json theme={null}
User → [Thinking] → Tool Call → Tool Result → Response
```

Cuando el razonamiento intercalado está habilitado, el modelo inserta un nuevo bloque de razonamiento cada vez que recibe el resultado de una herramienta, formando una cadena de razonamiento:

```json theme={null}
User → [Thinking] → Tool Call → Tool Result → [Thinking] → Response
                                                ↑ Interleaved Thinking
```

Esto permite al modelo:

* **Realizar razonamiento secundario basado en los resultados de la herramienta**, en lugar de concatenar simplemente las salidas.
* **Encadenar el razonamiento entre múltiples llamadas a herramientas**, donde cada decisión se basa en el análisis del paso anterior.

> Referencia: [Anthropic Interleaved Thinking](https://platform.claude.com/docs/en/build-with-claude/extended-thinking#interleaved-thinking)

### 1.2 Habilitar el razonamiento

Puedes habilitar el razonamiento de cuatro formas; elige cualquiera de ellas:

| Método                         | Ejemplo                              | Descripción                                                                            |
| :----------------------------- | :----------------------------------- | :------------------------------------------------------------------------------------- |
| `reasoning_effort`             | `"reasoning_effort": "low"`          | Parámetro estándar de OpenAI, colocado en el nivel superior del cuerpo de la solicitud |
| `reasoning.effort`             | `"reasoning": {"effort": "low"}`     | Equivalente al método anterior, colocado dentro del objeto reasoning                   |
| `reasoning.max_tokens`         | `"reasoning": {"max_tokens": 1024}`  | Controla con precisión el número máximo de tokens para el razonamiento                 |
| Nombre del modelo con `-think` | `"model": "claude-sonnet-4-5-think"` | La forma más sencilla; no requiere parámetros adicionales                              |

> **Prioridad** (cuando se usan varios métodos): `reasoning_effort` > `reasoning.max_tokens` > `reasoning.effort` > sufijo `-think`

**Valores posibles para effort:** `minimal` / `low` / `medium` / `high` / `xhigh`

### 1.3 Retorno del razonamiento

El mensaje de respuesta incluirá dos nuevos campos:

* `reasoning_content`: Contenido del razonamiento (cadena), para facilitar su visualización.
* `reasoning_details`: Información estructurada completa sobre el razonamiento, **que debe devolverse tal cual en conversaciones multiturno; la estructura interna puede diferir entre proveedores.**

Ejemplo sin streaming (omitiendo campos no relacionados):

```json theme={null}
{
  "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..."
      }
    }
  }]
}
```

En las respuestas en streaming, el contenido del razonamiento se enviará por fragmentos mediante `delta.reasoning_content` y `delta.reasoning_details`. Para la lógica completa de concatenación en streaming, consulta el ejemplo completo a continuación.

### 1.4 Conservar el razonamiento en conversaciones multiturno **(El razonamiento intercalado está integrado, no se necesitan parámetros adicionales)**

Para permitir que el modelo continúe sus capacidades de razonamiento en conversaciones multiturno, simplemente coloca el `reasoning_details` previamente devuelto **tal cual** en el mensaje del asistente de la siguiente ronda:

```json theme={null}
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 **habilitará automáticamente el razonamiento intercalado** cuando detecte información histórica de razonamiento en la solicitud, permitiendo que el modelo continúe el razonamiento profundo tras recibir los resultados de las llamadas a herramientas sin necesidad de parámetros adicionales.

### 1.5 Ejemplo completo

Los siguientes dos ejemplos muestran el proceso completo de Tool Call multiturno + razonamiento intercalado: consulta del usuario → el modelo piensa y llama a una herramienta → se inyectan los resultados de la herramienta (preservando `reasoning_details`) → el **razonamiento intercalado** del modelo da la respuesta final.

Sin streaming · Razonamiento intercalado

```python theme={null}
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 · Razonamiento intercalado

```python theme={null}
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 Reglas de mapeo de la intensidad del razonamiento

**Modo Effort:**

* Opus 4.6 / Sonnet 4.6 y superiores: se mapea al nivel de esfuerzo nativo de **Adaptive Thinking** de Anthropic.
* Otros modelos: se calcula usando la fórmula para `budget_tokens`:

```json theme={null}
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          |

**Mapeo del Adaptive Thinking Effort:**

| Effort entrante | Opus 4.6 | Sonnet 4.6 |
| :-------------- | :------- | :--------- |
| xhigh           | max      | high       |
| high            | high     | high       |
| medium          | medium   | medium     |
| low             | low      | low        |
| minimal         | low      | low        |

**Modo max\_tokens:** Se asigna directamente como el `budget_tokens` de Anthropic.

Sufijo `-think`: Opus/Sonnet 4.6+ usa el razonamiento adaptativo (effort=medium); otros modelos establecen `budget_tokens = min(10240, max_tokens - 1)`, con un `max_tokens` predeterminado de 4096.

***

## 2. Caché de prompts (Prompt Caching)

Puedes utilizar Prompt Caching al realizar solicitudes al modelo Claude a través de la interfaz Chat. Estableciendo puntos de corte `cache_control` en los mensajes, los bloques grandes de texto (como tarjetas de rol, datos de RAG, capítulos de libros, etc.) pueden almacenarse en caché para su reutilización, lo que permite que las solicitudes posteriores acierten directamente en la caché y reduzcan significativamente los costos.

> Documentación oficial de Claude: [Prompt Caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching)

### 2.1 Costos de caché

| Operación                       | Multiplicador de precio (relativo al precio de entrada original) |
| :------------------------------ | :--------------------------------------------------------------- |
| Escritura de caché (TTL 5 min)  | 1,25x                                                            |
| Escritura de caché (TTL 1 hora) | 2x                                                               |
| Lectura de caché                | 0,1x                                                             |

### 2.2 Modelos admitidos y longitud mínima de caché

| Modelo                                                                                | Cantidad mínima de tokens en caché |
| :------------------------------------------------------------------------------------ | :--------------------------------- |
| 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 (obsoleto) | 1024                               |
| Claude Haiku 4.5                                                                      | 4096                               |
| Claude Haiku 3.5 (obsoleto) / Haiku 3                                                 | 2048                               |

> **Límite de cantidad de puntos de corte:** Un máximo de **4** puntos de corte `cache_control` por solicitud.

### 2.3 TTL de la caché

| TTL                        | Sintaxis                                              | Escenarios aplicables                              |
| :------------------------- | :---------------------------------------------------- | :------------------------------------------------- |
| 5 minutos (predeterminado) | `"cache_control": {"type": "ephemeral"}`              | Sesiones cortas, solicitudes habituales            |
| 1 hora                     | `"cache_control": {"type": "ephemeral", "ttl": "1h"}` | Sesiones largas, para evitar reescrituras de caché |

Los costos de escritura para el TTL de 1 hora son mayores, pero pueden ahorrar gastos totales al reducir las reescrituras en sesiones extensas. Todos los modelos de Claude 4.5 y posteriores, de todos los proveedores (incluidos Anthropic, Amazon Bedrock y Google Vertex AI), admiten el TTL de 1 hora.

### 2.4 Uso

Puedes establecer puntos de corte de caché usando el campo `cache_control` en `system`, `user` (incluidas imágenes) y `tools`. Los siguientes ejemplos solo muestran la estructura clave, omitiendo grandes bloques de texto.

**Caché de mensajes del sistema (TTL predeterminado de 5 minutos):**

```json theme={null}
{
  "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"}]
    }
  ]
}
```

**Caché de mensajes del usuario (TTL de 1 hora):**

```json theme={null}
{
  "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"}
      ]
    }
  ]
}
```

**Caché de mensajes con imagen:**

```json theme={null}
{
  "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?"}
  ]
}
```

**Caché de la definición de herramientas:**

`cache_control` se coloca en el nivel superior del objeto de la herramienta (junto a `type` y `function`):

```json theme={null}
{
  "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 Consultar el estado de la caché

El campo `usage` de la respuesta devolverá `claude_cache_tokens_details`, que registra información detallada de la caché:

**Primera solicitud (creando la caché):**

```json theme={null}
{
  "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
    }
  }
}
```

**Solicitudes posteriores (acierto de caché):**

```json theme={null}
{
  "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
    }
  }
}
```

| Campo                                 | Significado                                                                               |
| :------------------------------------ | :---------------------------------------------------------------------------------------- |
| `cache_creation_input_tokens`         | Número de tokens escritos en la caché en esta solicitud                                   |
| `cache_read_input_tokens`             | Número de tokens leídos desde la caché en esta solicitud                                  |
| `cache_write_5_minutes_input_tokens`  | Número de tokens escritos en la caché con TTL de 5 minutos                                |
| `cache_write_1_hour_input_tokens`     | Número de tokens escritos en la caché con TTL de 1 hora                                   |
| `prompt_tokens_details.cached_tokens` | Número de tokens en caché cuando se acierta la caché, compatible con el formato de OpenAI |

***

## 3. Cabecera de solicitud para anthropic-beta

Puedes habilitar las funciones beta del modelo Claude mediante la cabecera HTTP `anthropic-beta`, que AihubMix reenviará a la API de Anthropic.

### Uso

Añade `anthropic-beta` a la cabecera de la solicitud, con el valor del identificador de la función beta correspondiente:

```json theme={null}
curl "https://aihubmix.com/v1/chat/completions" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer sk-***" \
  -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"}]}
  ]
}'
```

> Para identificadores beta específicos disponibles, consulta la [documentación de la API de Anthropic](https://docs.anthropic.com/en/api/beta).

***

Última actualización: 2026-06-01
