Оновлення OpenAI-сумісного інтерфейсу: глибока підтримка Claude

AIHubMix оновив свій OpenAI-сумісний API для Claude: чергування мислення без додаткових параметрів, кешування промптів і підтримка бета-функцій Anthropic.

AIHubMix8 хв читання
Оновлення OpenAI-сумісного інтерфейсу: глибока підтримка Claude

Ми оновили OpenAI-сумісний інтерфейс із глибшою оптимізацією саме для моделей серії Claude. Тепер ви можете точніше й зручніше керувати мисленням і кешуванням. Чергування мислення в багатоходових розмовах стало зручнішим: його можна інтегрувати без додаткових параметрів. Також підтримується ввімкнення бета-функцій, які пропонує Anthropic.

1. Мислення моделі (Extended Thinking)

1.1 Переваги чергування мислення

Коли чергування мислення не увімкнено, модель виконує мислення лише один раз на початку ходу асистента; наступні відповіді генеруються безпосередньо після отримання результатів інструментів, без створення нових блоків мислення:

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

Коли чергування мислення увімкнено, модель вставляє новий блок мислення щоразу, коли отримує результат інструмента, формуючи ланцюжок міркувань:

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

Це дозволяє моделі:

  • виконувати вторинні міркування на основі результатів інструментів, а не просто об’єднувати вихідні дані.
  • ланцюжково міркувати між кількома викликами інструментів, де кожне рішення ґрунтується на аналізі попереднього кроку.
Довідка: Anthropic Interleaved Thinking

1.2 Увімкнення мислення

Мислення можна увімкнути чотирма способами, обравши будь-який із них:

Спосіб Приклад Опис
reasoning_effort "reasoning_effort": "low" Стандартний параметр OpenAI, розміщений на верхньому рівні тіла запиту
reasoning.effort "reasoning": {"effort": "low"} Еквівалентний попередньому способу, розміщений в об’єкті reasoning
reasoning.max_tokens "reasoning": {"max_tokens": 1024} Точно контролює максимальну кількість токенів для мислення
Назва моделі з -think "model": "claude-sonnet-4-5-think" Найпростіший спосіб, не потребує додаткових параметрів
Пріоритет (коли використовується кілька способів): reasoning_effort > reasoning.max_tokens > reasoning.effort > суфікс -think

Можливі значення effort: minimal / low / medium / high / xhigh

1.3 Повернення мислення

Повідомлення відповіді міститиме два нові поля:

  • reasoning_content: вміст мислення (рядок) для зручного відображення.
  • reasoning_details: повна структурована інформація про мислення, яку потрібно повертати без змін у багатоходових розмовах; внутрішня структура може відрізнятися між провайдерами.

Приклад для непотокової відповіді (нерелевантні поля опущено):

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

У потокових відповідях вміст мислення надсилатиметься частинами через delta.reasoning_content і delta.reasoning_details. Повну логіку конкатенації потоку див. у повному прикладі нижче.

1.4 Збереження мислення в багатоходових розмовах (чергування мислення вбудовано, додаткові параметри не потрібні)

Щоб модель продовжувала використовувати свої міркувальні можливості в багатоходових розмовах, просто вставте раніше повернуті reasoning_details без змін у повідомлення асистента наступного раунду:

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 автоматично увімкне чергування мислення, коли виявить у запиті історичну інформацію про мислення, що дозволить моделі продовжувати глибокі міркування після отримання результатів викликів інструментів без додаткових параметрів.

1.5 Повний приклад

Наведені два приклади демонструють повний багатоходовий процес Tool Call + чергування мислення: запит користувача → модель міркує та викликає інструмент → додавання результатів інструмента (із збереженням reasoning_details) → модель чергує мислення і дає фінальну відповідь.

Непотокове · Чергування мислення

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})

Потокове · Чергування мислення

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 Правила відображення інтенсивності мислення

Режим effort:

  • Opus 4.6 / Sonnet 4.6 і новіші: відображаються на рівень effort рідного Adaptive Thinking від Anthropic.
  • Інші моделі: обчислюються за формулою для 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

Відображення effort для Adaptive Thinking:

Вхідний effort Opus 4.6 Sonnet 4.6
xhigh max high
high high high
medium medium medium
low low low
minimal low low

Режим max_tokens: напряму призначається як budget_tokens від Anthropic.

-think суфікс: Opus/Sonnet 4.6+ використовують adaptive thinking (effort=medium); для інших моделей budget_tokens = min(10240, max_tokens - 1), а типове значення max_tokens становить 4096.


2. Кешування промптів

Кешування промптів можна використовувати під час запитів до моделі Claude через інтерфейс Chat. Встановлюючи точки переривання cache_control у повідомленнях, великі блоки тексту (наприклад, картки персонажів, дані RAG, розділи книг тощо) можна кешувати для повторного використання, а наступні запити будуть напряму звертатися до кешу, що суттєво знижує витрати.

Офіційна документація Claude: Prompt Caching

2.1 Вартість кешування

Операція Множник ціни (відносно початкової ціни вхідних токенів)
Запис у кеш (TTL 5 хвилин) 1.25x
Запис у кеш (TTL 1 година) 2x
Читання з кешу 0.1x

2.2 Підтримувані моделі та мінімальна довжина кешу

Модель Мінімальна кількість токенів для кешу
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 (застаріла) 1024
Claude Haiku 4.5 4096
Claude Haiku 3.5 (застаріла) / Haiku 3 2048
Обмеження кількості точок переривання: не більше 4 точок cache_control на запит.

2.3 TTL кешу

TTL Синтаксис Сценарії використання
5 хвилин (за замовчуванням) "cache_control": {"type": "ephemeral"} Короткі сесії, звичайні запити
1 година "cache_control": {"type": "ephemeral", "ttl": "1h"} Довгі сесії, щоб уникнути повторних записів у кеш

Вартість запису з TTL 1 година вища, але вона може знизити загальні витрати, зменшуючи повторні записи в довгих сесіях. Усі моделі Claude 4.5 і новіших від усіх провайдерів (включно з Anthropic, Amazon Bedrock, Google Vertex AI) підтримують TTL 1 годину.

2.4 Використання

Точки кешування можна встановлювати через поле cache_control у system, user (включно із зображеннями) і tools. Наведені нижче приклади показують лише структуру ключів, без великих блоків тексту.

Кешування системного повідомлення (TTL 5 хвилин за замовчуванням):

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

Кешування повідомлення користувача (TTL 1 година):

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

Кешування повідомлення із зображенням:

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

Кешування визначення інструмента:

cache_control розміщується на верхньому рівні об’єкта інструмента (поруч із type і 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 Перегляд стану кешу

У полі usage відповіді повертається claude_cache_tokens_details з детальною інформацією про кеш:

Перший запит (створення кешу):

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

Наступні запити (попадання в кеш):

{
  "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
    }
  }
}
Поле Значення
cache_creation_input_tokens Кількість токенів, записаних у кеш у цьому запиті
cache_read_input_tokens Кількість токенів, прочитаних із кешу в цьому запиті
cache_write_5_minutes_input_tokens Кількість токенів, записаних у кеш із TTL 5 хвилин
cache_write_1_hour_input_tokens Кількість токенів, записаних у кеш із TTL 1 година
prompt_tokens_details.cached_tokens Кількість токенів із кешу при попаданні, сумісна з форматом OpenAI

3. Заголовок запиту anthropic-beta

Бета-функції моделі Claude можна увімкнути через HTTP-заголовок anthropic-beta, який AIHubMix передає до API Anthropic.

Використання

Додайте anthropic-beta до заголовка запиту, а як значення вкажіть відповідний ідентифікатор бета-функції:

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"}]}
  ]
}'
Актуальні ідентифікатори бета-функцій дивіться в документації Anthropic API.

Останнє оновлення: 2026-06-01

Один шлюз до багатьох провайдерів ШІ. Створено для продакшену.

Перейти на AIHubMix