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

# Reasoning Tokens

For models that support it, the Anannas API can return **Reasoning Tokens**, also known as thinking tokens. Anannas normalizes the different ways of customizing the amount of reasoning tokens that the model will use, providing a unified interface across different providers.

Reasoning tokens provide a transparent look into the reasoning steps taken by a model. Reasoning tokens are considered output tokens and charged accordingly.

Reasoning tokens are included in the response by default if the model decides to output them. Reasoning tokens will appear in the `reasoning` field of each message, unless you decide to exclude them.

<Note>
  **Some reasoning models do not return their reasoning tokens**

  While most models and providers make reasoning tokens available in the response, some (like the OpenAI o-series and Gemini Flash Thinking) do not.
</Note>

### **Controlling Reasoning Tokens**

You can control reasoning tokens in your requests using the `reasoning` parameter:

```json theme={null}
{
  "model": "your-model",
  "messages": [],
  "reasoning": {
    // One of the following (not both):
    "effort": "high", // Can be "high", "medium", or "low" (OpenAI-style)
    "max_tokens": 2000, // Specific token limit (Anthropic-style)

    // Optional: Default is false. All models support this.
    "exclude": false, // Set to true to exclude reasoning tokens from response

    // Or enable reasoning with the default parameters:
    "enabled": true // Default: inferred from `effort` or `max_tokens`
  }
}
```

The `reasoning` config object consolidates settings for controlling reasoning strength across different models. See the Note for each option below to see which models are supported and how other models will behave.

### **Max Tokens for Reasoning**

<Card title="Check Reasoning Support" icon="book">
  For models that support `max_tokens` reasoning configuration, visit [anannas.ai/models](https://anannas.ai/models) to see model-specific support.
</Card>

<Note>
  **Currently supported by:**

  * Gemini thinking models
  * Anthropic reasoning models (by using the `reasoning.max_tokens` parameter)
  * Some Alibaba Qwen thinking models (mapped to `thinking_budget`)

  For Alibaba, support varies by model — please check the individual model descriptions to confirm whether `reasoning.max_tokens` (via `thinking_budget`) is available.
</Note>

For models that support reasoning token allocation, you can control it like this:

* `"max_tokens": 2000` - Directly specifies the maximum number of tokens to use for reasoning

For models that only support `reasoning.effort` (see below), the `max_tokens` value will be used to determine the effort level.

### **Reasoning Effort Level**

<Card title="Check Reasoning Model Support" icon="book">
  For models that support reasoning and their specific capabilities, visit [anannas.ai/models](https://anannas.ai/models).
</Card>

<Note>
  **Supported models**

  Currently supported by OpenAI reasoning models (o1 series, o3 series, GPT-5 series) and Grok models
</Note>

* `"effort": "high"` - Allocates a large portion of tokens for reasoning (approximately 80% of max\_tokens)
* `"effort": "medium"` - Allocates a moderate portion of tokens (approximately 50% of max\_tokens)
* `"effort": "low"` - Allocates a smaller portion of tokens (approximately 20% of max\_tokens)

For models that only support `reasoning.max_tokens`, the effort level will be set based on the percentages above.

### **Excluding Reasoning Tokens**

If you want the model to use reasoning internally but not include it in the response:

* `"exclude": true` - The model will still use reasoning, but it won’t be returned in the response

Reasoning tokens will appear in the `reasoning` field of each message.

### **Enable Reasoning with Default Config**

To enable reasoning with the default parameters:

* `"enabled": true` - Enables reasoning at the “medium” effort level with no exclusions.

We recommend using the new unified `reasoning` parameter for better control and future compatibility.

### **Examples**

### **Basic Usage with Reasoning Tokens**

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  url = "https://api.anannas.ai/v1/chat/completions"
  headers = {
      "Authorization": f"Bearer <ANANNAS_API_KEY>",
      "Content-Type": "application/json"
  }
  payload = {
      "model": "openai/o3-mini",
      "messages": [
          {"role": "user", "content": "How would you build the world's tallest skyscraper?"}
      ],
      "reasoning": {
          "effort": "high"  # Use high reasoning effort
      }
  }

  response = requests.post(url, headers=headers, data=json.dumps(payload))
  print(response.json()['choices'][0]['message']['reasoning'])
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey: '<ANANNAS_API_KEY>',
  });

  async function getResponseWithReasoning() {
    const response = await openai.chat.completions.create({
      model: 'openai/o3-mini',
      messages: [
        {
          role: 'user',
          content: "How would you build the world's tallest skyscraper?",
        },
      ],
      reasoning: {
        effort: 'high', // Use high reasoning effort
      },
    });

    console.log('REASONING:', response.choices[0].message.reasoning);
    console.log('CONTENT:', response.choices[0].message.content);
  }

  getResponseWithReasoning();
  ```
</CodeGroup>

### **Using Max Tokens for Reasoning**

For models that support direct token allocation (like Anthropic models), you can specify the exact number of tokens to use for reasoning:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  url = "https://api.anannas.ai/v1/chat/completions"
  headers = {
      "Authorization": f"Bearer <ANANNAS_API_KEY>",
      "Content-Type": "application/json"
  }
  payload = {
      "model": "anthropic/claude-3.7-sonnet",
      "messages": [
          {"role": "user", "content": "What's the most efficient algorithm for sorting a large dataset?"}
      ],
      "reasoning": {
          "max_tokens": 2000  # Allocate 2000 tokens (or approximate effort) for reasoning
      }
  }

  response = requests.post(url, headers=headers, data=json.dumps(payload))
  print(response.json()['choices'][0]['message']['reasoning'])
  print(response.json()['choices'][0]['message']['content'])
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey: '<ANANNAS_API_KEY>',
  });

  async function getResponseWithReasoning() {
    const response = await openai.chat.completions.create({
      model: 'anthropic/claude-3.7-sonnet',
      messages: [
        {
          role: 'user',
          content: "How would you build the world's tallest skyscraper?",
        },
      ],
      reasoning: {
        max_tokens: 2000, // Allocate 2000 tokens (or approximate effort) for reasoning
      },
    });

    console.log('REASONING:', response.choices[0].message.reasoning);
    console.log('CONTENT:', response.choices[0].message.content);
  }

  getResponseWithReasoning();
  ```
</CodeGroup>

### **Excluding Reasoning Tokens from Response**

If you want the model to use reasoning internally but not include it in the response:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  url = "https://api.anannas.ai/v1/chat/completions"
  headers = {
      "Authorization": f"Bearer <ANANNAS_API_KEY>",
      "Content-Type": "application/json"
  }
  payload = {
      "model": "deepseek/deepseek-r1",
      "messages": [
          {"role": "user", "content": "Explain quantum computing in simple terms."}
      ],
      "reasoning": {
          "effort": "high",
          "exclude": true  # Use reasoning but don't include it in the response
      }
  }

  response = requests.post(url, headers=headers, data=json.dumps(payload))
  # No reasoning field in the response
  print(response.json()['choices'][0]['message']['content'])
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey: '<ANANNAS_API_KEY>',
  });

  async function getResponseWithReasoning() {
    const response = await openai.chat.completions.create({
      model: 'deepseek/deepseek-r1',
      messages: [
        {
          role: 'user',
          content: "How would you build the world's tallest skyscraper?",
        },
      ],
      reasoning: {
        effort: 'high',
        exclude: true, // Use reasoning but don't include it in the response
      },
    });

    console.log('REASONING:', response.choices[0].message.reasoning);
    console.log('CONTENT:', response.choices[0].message.content);
  }

  getResponseWithReasoning();
  ```
</CodeGroup>

### **Advanced Usage: Reasoning Chain-of-Thought**

This example shows how to use reasoning tokens in a more complex workflow. It injects one model’s reasoning into another model to improve its response quality:

<CodeGroup>
  ```python Python theme={null}
  import requests
  import json

  question = "Which is bigger: 9.11 or 9.9?"

  url = "https://api.anannas.ai/v1/chat/completions"
  headers = {
      "Authorization": f"Bearer <ANANNAS_API_KEY>",
      "Content-Type": "application/json"
  }

  def do_req(model, content, reasoning_config=None):
      payload = {
          "model": model,
          "messages": [
              {"role": "user", "content": content}
          ],
          "stop": "</think>"
      }

      return requests.post(url, headers=headers, data=json.dumps(payload))

  # Get reasoning from a capable model
  content = f"{question} Please think this through, but don't output an answer"
  reasoning_response = do_req("deepseek/deepseek-r1", content)
  reasoning = reasoning_response.json()['choices'][0]['message']['reasoning']

  # Let's test! Here's the naive response:
  simple_response = do_req("openai/gpt-5-mini", question)
  print(simple_response.json()['choices'][0]['message']['content'])

  # Here's the response with the reasoning token injected:
  content = f"{question}. Here is some context to help you: {reasoning}"
  smart_response = do_req("openai/gpt-5-mini", content)
  print(smart_response.json()['choices'][0]['message']['content'])
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey,
  });

  async function doReq(model, content, reasoningConfig) {
    const payload = {
      model,
      messages: [{ role: 'user', content }],
      stop: '</think>',
      ...reasoningConfig,
    };

    return openai.chat.completions.create(payload);
  }

  async function getResponseWithReasoning() {
    const question = 'Which is bigger: 9.11 or 9.9?';
    const reasoningResponse = await doReq(
      'deepseek/deepseek-r1',
      `${question} Please think this through, but don't output an answer`,
    );
    const reasoning = reasoningResponse.choices[0].message.reasoning;

    // Let's test! Here's the naive response:
    const simpleResponse = await doReq('openai/gpt-5-mini', question);
    console.log(simpleResponse.choices[0].message.content);

    // Here's the response with the reasoning token injected:
    const content = `${question}. Here is some context to help you: ${reasoning}`;
    const smartResponse = await doReq('openai/gpt-5-mini', content);
    console.log(smartResponse.choices[0].message.content);
  }

  getResponseWithReasoning();
  ```
</CodeGroup>

## **Provider-Specific Reasoning Implementation**

### **Anthropic Models with Reasoning Tokens**

The latest Claude models, such as [**<u>anthropic/claude-3.7-sonnet</u>**](https://anannas.ai/models/anthropic/claude-3.7-sonnet), support working with and returning reasoning tokens.

You can enable reasoning on Anthropic models **only** using the unified `reasoning`parameter with either `effort` or `max_tokens`.

**Note:** The `:thinking` variant is no longer supported for Anthropic models. Use the `reasoning` parameter instead.

#### **Reasoning Max Tokens for Anthropic Models**

<Card title="Check Reasoning Limits" icon="book">
  For model-specific reasoning token limits and allocation rules, visit [anannas.ai/models](https://anannas.ai/models).
</Card>

When using Anthropic models with reasoning:

* When using the `reasoning.max_tokens` parameter, that value is used directly with a minimum of 1024 tokens.
* When using the `reasoning.effort` parameter, the budget\_tokens are calculated based on the `max_tokens` value.

The reasoning token allocation is capped at 32,000 tokens maximum and 1024 tokens minimum. The formula for calculating the budget\_tokens is: `budget_tokens = max(min(max_tokens * {effort_ratio}, 32000), 1024)`

effort\_ratio is 0.8 for high effort, 0.5 for medium effort, and 0.2 for low effort.

**Important**:

* When using reasoning **without tools**, `max_tokens` must be strictly higher than the reasoning budget to ensure there are tokens available for the final response after thinking.
* When using reasoning **with tools** (interleaved thinking), the `budget_tokens` can exceed `max_tokens` as it represents the total budget across all thinking blocks within one assistant turn.

**Restrictions when reasoning is enabled:**

* `tool_choice` can only be `"auto"` or `"none"` (not `"required"` or specific tool)
* `top_k` parameter is not allowed
* Pre-filled assistant messages (assistant messages with content) are not allowed

<Note>
  **Token Usage and Billing**

  Please note that reasoning tokens are counted as output tokens for billing purposes. Using reasoning tokens will increase your token usage but can significantly improve the quality of model responses.
</Note>

#### **Interleaved Thinking with Tool Use**

<Card title="Model Support" icon="book">
  Interleaved thinking is automatically enabled for Claude 4 models (Sonnet 4.5, Opus 4.5, Haiku 4.5) when reasoning is enabled with tools. Visit [anannas.ai/models](https://anannas.ai/models) to see which models support this feature.
</Card>

Interleaved thinking allows the model to reason between tool calls, enabling more sophisticated reasoning after receiving tool results. This feature is automatically enabled when:

* Reasoning/thinking is enabled (`reasoning.max_tokens` or `reasoning.effort`)
* Tools are present in the request
* Tool choice is `"auto"` (or not specified, which defaults to auto)

**Key Benefits:**

* **Reasoning between tool calls**: The model can think about tool results before deciding what to do next
* **Chained tool calls**: Enables more nuanced decisions based on intermediate results
* **Flexible token allocation**: With interleaved thinking, `budget_tokens` can exceed `max_tokens` as it represents the total budget across all thinking blocks

**Example: Interleaved Thinking with Tools**

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.anannas.ai/v1",
      api_key="<ANANNAS_API_KEY>",
  )

  response = client.chat.completions.create(
      model="anthropic/claude-sonnet-4-5-20250929",
      messages=[
          {
              "role": "user",
              "content": "What's the weather in Paris? Also tell me the current time there."
          }
      ],
      tools=[
          {
              "type": "function",
              "function": {
                  "name": "get_weather",
                  "description": "Get weather for a location",
                  "parameters": {
                      "type": "object",
                      "properties": {
                          "location": {"type": "string"}
                      },
                      "required": ["location"]
                  }
              }
          },
          {
              "type": "function",
              "function": {
                  "name": "get_time",
                  "description": "Get current time for a location",
                  "parameters": {
                      "type": "object",
                      "properties": {
                          "location": {"type": "string"}
                      },
                      "required": ["location"]
                  }
              }
          }
      ],
      tool_choice="auto",  # Required for interleaved thinking
      reasoning={
          "max_tokens": 5000  # Can exceed max_tokens when interleaved thinking is enabled
      },
      max_tokens=2000
  )

  # The model will reason before each tool call and after receiving results
  print(response.choices[0].message.reasoning)
  print(response.choices[0].message.tool_calls)
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const client = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey: '<ANANNAS_API_KEY>',
  });

  const response = await client.chat.completions.create({
    model: 'anthropic/claude-sonnet-4-5-20250929',
    messages: [
      {
        role: 'user',
        content: "What's the weather in Paris? Also tell me the current time there.",
      },
    ],
    tools: [
      {
        type: 'function',
        function: {
          name: 'get_weather',
          description: 'Get weather for a location',
          parameters: {
            type: 'object',
            properties: {
              location: { type: 'string' },
            },
            required: ['location'],
          },
        },
      },
      {
        type: 'function',
        function: {
          name: 'get_time',
          description: 'Get current time for a location',
          parameters: {
            type: 'object',
            properties: {
              location: { type: 'string' },
            },
            required: ['location'],
          },
        },
      },
    ],
    tool_choice: 'auto', // Required for interleaved thinking
    reasoning: {
      max_tokens: 5000, // Can exceed max_tokens when interleaved thinking is enabled
    },
    max_tokens: 2000,
  });

  // The model will reason before each tool call and after receiving results
  console.log(response.choices[0].message.reasoning);
  console.log(response.choices[0].message.tool_calls);
  ```
</CodeGroup>

<Note>
  **Tool Choice Restrictions with Thinking**

  When reasoning is enabled, only `tool_choice: "auto"` or `tool_choice: "none"` are supported. Using `tool_choice: "required"` or forcing a specific tool will result in an error.
</Note>

### **Examples with Anthropic Models**

#### **Example 1: Streaming mode with reasoning tokens**

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.anannas.ai/v1",
      api_key="<ANANNAS_API_KEY>",
  )

  def chat_completion_with_reasoning(messages):
      response = client.chat.completions.create(
          model="anthropic/claude-3.7-sonnet",
          messages=messages,
          max_tokens=10000,
          reasoning={
              "max_tokens": 8000  # Directly specify reasoning token budget
          },
          stream=True
      )
      return response

  for chunk in chat_completion_with_reasoning([
      {"role": "user", "content": "What's bigger, 9.9 or 9.11?"}
  ]):
      if hasattr(chunk.choices[0].delta, 'reasoning') and chunk.choices[0].delta.reasoning:
          print(f"REASONING: {chunk.choices[0].delta.reasoning}")
      elif chunk.choices[0].delta.content:
          print(f"CONTENT: {chunk.choices[0].delta.content}")
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const openai = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey,
  });

  async function chatCompletionWithReasoning(messages) {
    const response = await openai.chat.completions.create({
      model: '{{MODEL}}',
      messages,
      maxTokens: 10000,
      reasoning: {
        maxTokens: 8000, // Directly specify reasoning token budget
      },
      stream: true,
    });

    return response;
  }

  (async () => {
    for await (const chunk of chatCompletionWithReasoning([
      { role: 'user', content: "What's bigger, 9.9 or 9.11?" },
    ])) {
      if (chunk.choices[0].delta.reasoning) {
        console.log(`REASONING: ${chunk.choices[0].delta.reasoning}`);
      } else if (chunk.choices[0].delta.content) {
        console.log(`CONTENT: ${chunk.choices[0].delta.content}`);
      }
    }
  })();
  ```
</CodeGroup>

### **Preserving Reasoning Blocks**

<Note>
  **Model Support**

  The reasoning\_details are currently returned by all OpenAI reasoning models (o1 series, o3 series, GPT-5 series) and all Anthropic reasoning models (Claude 3.7, Claude 4, and Claude 4.1 series).
</Note>

The reasoning\_details functionality works identically across all supported reasoning models. You can easily switch between OpenAI reasoning models (like `openai/gpt-5-mini`) and Anthropic reasoning models (like `anthropic/claude-sonnet-4`) without changing your code structure.

If you want to pass reasoning back in context, you must pass reasoning blocks back to the API. This is useful for maintaining the model’s reasoning flow and conversation integrity.

Preserving reasoning blocks is useful specifically for tool calling. When models like Claude invoke tools, it is pausing its construction of a response to await external information. When tool results are returned, the model will continue building that existing response. This necessitates preserving reasoning blocks during tool use, for a couple of reasons:

**Reasoning continuity**: The reasoning blocks capture the model’s step-by-step reasoning that led to tool requests. When you post tool results, including the original reasoning ensures the model can continue its reasoning from where it left off.

**Context maintenance**: While tool results appear as user messages in the API structure, they’re part of a continuous reasoning flow. Preserving reasoning blocks maintains this conceptual flow across multiple API calls.

<Note>
  **Important for Reasoning Models**

  When providing reasoning\_details blocks, the entire sequence of consecutive reasoning blocks must match the outputs generated by the model during the original request; you cannot rearrange or modify the sequence of these blocks.
</Note>

## **Responses API Shape**

When reasoning models generate responses, the reasoning information is structured in a standardized format through the `reasoning_details` array. This section documents the API response structure for reasoning details in both streaming and non-streaming responses, based on the schema definitions in the `llm-interfaces` package.

### **reasoning\_details Array Structure**

The `reasoning_details` field contains an array of reasoning detail objects. Each object in the array represents a specific piece of reasoning information and follows one of three possible types. The location of this array differs between streaming and non-streaming responses:

* **Non-streaming responses**: `reasoning_details` appears in `choices[].message.reasoning_details`
* **Streaming responses**: `reasoning_details` appears in `choices[].delta.reasoning_details` for each chunk

#### **Common Fields**

All reasoning detail objects share these common fields:

* `id` (string | null): Unique identifier for the reasoning detail
* `format` (string): The format of the reasoning detail, with possible values:
  * `"unknown"` - Format is not specified
  * `"openai-responses-v1"` - OpenAI responses format version 1
  * `"anthropic-claude-v1"` - Anthropic Claude format version 1 (default)
* `index` (number, optional): Sequential index of the reasoning detail

#### **Reasoning Detail Types**

**1. Summary Type (**`reasoning.summary`**)**

Contains a high-level summary of the reasoning process:

```json theme={null}
{
  "type": "reasoning.summary",
  "summary": "The model analyzed the problem by first identifying key constraints, then evaluating possible solutions...",
  "id": "reasoning-summary-1",
  "format": "anthropic-claude-v1",
  "index": 0
}
```

**2. Encrypted Type (**`reasoning.encrypted`**)**

Contains encrypted reasoning data that may be redacted or protected:

```json theme={null}
{  
	"type": "reasoning.encrypted",  
	"data": "eyJlbmNyeXB0ZWQiOiJ0cnVlIiwiY29udGVudCI6IltSRURBQ1RFRF0ifQ==",  
	"id": "reasoning-encrypted-1",  
	"format": "anthropic-claude-v1",  
	"index": 1
}
```

**3. Text Type (**`reasoning.text`**)**

Contains raw text reasoning with optional signature verification:

```json theme={null}
{  
	"type": "reasoning.text",  
	"text": "Let me think through this step by step:\n1. First, I need to understand the user's question...",  
	"signature": "sha256:abc123def456...",  
	"id": "reasoning-text-1",  
	"format": "anthropic-claude-v1",  
	"index": 2
}
```

### **Response Examples**

#### **Non-Streaming Response**

In non-streaming responses, `reasoning_details` appears in the message:

<CodeGroup>
  ```json filename theme={null}
  // add code here{
    "choices": [
      {
        "message": {
          "role": "assistant",
          "content": "Based on my analysis, I recommend the following approach...",
          "reasoning_details": [
            {
              "type": "reasoning.summary",
              "summary": "Analyzed the problem by breaking it into components",
              "id": "reasoning-summary-1",
              "format": "anthropic-claude-v1",
              "index": 0
            },
            {
              "type": "reasoning.text",
              "text": "Let me work through this systematically:\n1. First consideration...\n2. Second consideration...",
              "signature": null,
              "id": "reasoning-text-1",
              "format": "anthropic-claude-v1",
              "index": 1
            }
          ]
        }
      }
    ]
  }
  ```
</CodeGroup>

#### **Streaming Response**

In streaming responses, `reasoning_details` appears in delta chunks as the reasoning is generated:

<CodeGroup>
  ```json filename theme={null}
  {
    "choices": [
      {
        "delta": {
          "reasoning_details": [
            {
              "type": "reasoning.text",
              "text": "Let me think about this step by step...",
              "signature": null,
              "id": "reasoning-text-1",
              "format": "anthropic-claude-v1",
              "index": 0
            }
          ]
        }
      }
    ]
  }
  ```
</CodeGroup>

**Streaming Behavior Notes:**

* Each reasoning detail chunk is sent as it becomes available
* The `reasoning_details` array in each chunk may contain one or more reasoning objects
* For encrypted reasoning, the content may appear as `[REDACTED]` in streaming responses
* The complete reasoning sequence is built by concatenating all chunks in order

### **Example: Preserving Reasoning Blocks with Anannas AI and Claude**

<CodeGroup>
  ```python Python theme={null}
  from openai import OpenAI

  client = OpenAI(
      base_url="https://api.anannas.ai/v1",
      api_key="<ANANNAS_API_KEY>",
  )

  # First API call with tools
  # Note: You can use 'openai/gpt-5-mini' instead of 'anthropic/claude-sonnet-4' - they're completely interchangeable
  response = client.chat.completions.create(
      model="anthropic/claude-sonnet-4",
      messages=[
          {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."}
      ],
      tools=[{
          "type": "function",
          "function": {
              "name": "get_weather",
              "description": "Get current weather",
              "parameters": {
                  "type": "object",
                  "properties": {
                      "location": {"type": "string"}
                  },
                  "required": ["location"]
              }
          }
      }],
      reasoning={"max_tokens": 2000}
  )

  # Extract the assistant message with reasoning_details
  message = response.choices[0].message

  # Preserve the complete reasoning_details when passing back
  messages = [
      {"role": "user", "content": "What's the weather like in Boston? Then recommend what to wear."},
      {
          "role": "assistant",
          "content": message.content,
          "tool_calls": message.tool_calls,
          "reasoning_details": message.reasoning_details  # Pass back unmodified
      },
      {
          "role": "tool",
          "tool_call_id": message.tool_calls[0].id,
          "content": '{"temperature": 45, "condition": "rainy", "humidity": 85}'
      }
  ]

  # Second API call - Claude continues reasoning from where it left off
  response2 = client.chat.completions.create(
      model="anthropic/claude-sonnet-4",
      messages=messages,  # Includes preserved thinking blocks
      tools=tools
  )
  ```

  ```typescript TypeScript theme={null}
  import OpenAI from 'openai';

  const client = new OpenAI({
    baseURL: 'https://api.anannas.ai/v1',
    apiKey: '<ANANNAS_API_KEY>',
  });

  // First API call with tools
  // Note: You can use 'openai/gpt-5-mini' instead of 'anthropic/claude-sonnet-4' - they're completely interchangeable
  const response = await client.chat.completions.create({
    model: 'anthropic/claude-sonnet-4',
    messages: [
      {
        role: 'user',
        content:
          "What's the weather like in Boston? Then recommend what to wear.",
      },
    ],
    tools: [
      {
        type: 'function',
        function: {
          name: 'get_weather',
          description: 'Get current weather',
          parameters: {
            type: 'object',
            properties: {
              location: { type: 'string' },
            },
            required: ['location'],
          },
        },
      },
    ],
    reasoning: { max_tokens: 2000 },
  });

  // Extract the assistant message with reasoning_details
  const message = response.choices[0].message;

  // Preserve the complete reasoning_details when passing back
  const messages = [
    {
      role: 'user',
      content: "What's the weather like in Boston? Then recommend what to wear.",
    },
    {
      role: 'assistant',
      content: message.content,
      tool_calls: message.tool_calls,
      reasoning_details: message.reasoning_details, // Pass back unmodified
    },
    {
      role: 'tool',
      tool_call_id: message.tool_calls[0].id,
      content: JSON.stringify({
        temperature: 45,
        condition: 'rainy',
        humidity: 85,
      }),
    },
  ];

  // Second API call - Claude continues reasoning from where it left off
  const response2 = await client.chat.completions.create({
    model: 'anthropic/claude-sonnet-4',
    messages, // Includes preserved thinking blocks
    tools,
  });
  ```
</CodeGroup>

<div
  style={{ 
marginTop: '2rem',
display: 'flex',
alignItems: 'center',
gap: '0.75rem',
fontSize: '14px',
color: '#6b7280'
}}
>
  <span>Was this page helpful?</span>

  <button
    id="feedback-yes-btn"
    onClick={() => {
  console.log('Positive feedback');
  document.getElementById('feedback-yes-btn').style.background = 'rgba(156, 163, 175, 0.1)';
  document.getElementById('feedback-yes-btn').style.backdropFilter = 'blur(10px)';
  document.getElementById('feedback-yes-btn').style.border = '1px solid rgba(156, 163, 175, 0.2)';
  document.getElementById('feedback-no-btn').style.background = 'transparent';
  document.getElementById('feedback-no-btn').style.backdropFilter = 'none';
  document.getElementById('feedback-no-btn').style.border = 'none';
}}
    style={{
  background: 'transparent',
  border: 'none',
  cursor: 'pointer',
  padding: '6px 12px',
  fontSize: '14px',
  borderRadius: '8px',
  transition: 'all 0.2s ease',
  display: 'flex',
  alignItems: 'center',
  gap: '6px',
  color: '#6b7280'
}}
  >
    <span style={{ fontSize: '16px' }}>👍</span>
    Yes
  </button>

  <button
    id="feedback-no-btn"
    onClick={() => {
  console.log('Negative feedback');
  document.getElementById('feedback-no-btn').style.background = 'rgba(156, 163, 175, 0.1)';
  document.getElementById('feedback-no-btn').style.backdropFilter = 'blur(10px)';
  document.getElementById('feedback-no-btn').style.border = '1px solid rgba(156, 163, 175, 0.2)';
  document.getElementById('feedback-yes-btn').style.background = 'transparent';
  document.getElementById('feedback-yes-btn').style.backdropFilter = 'none';
  document.getElementById('feedback-yes-btn').style.border = 'none';
}}
    style={{
  background: 'transparent',
  border: 'none',
  cursor: 'pointer',
  padding: '6px 12px',
  fontSize: '14px',
  borderRadius: '8px',
  transition: 'all 0.2s ease',
  display: 'flex',
  alignItems: 'center',
  gap: '6px',
  color: '#6b7280'
}}
  >
    <span style={{ fontSize: '16px' }}>👎</span>
    No
  </button>
</div>
