1. SDK
  2. Thinking (Reasoning)

Pass a thinking object on send_message / sendMessage to let the model reason before responding. Omit it or set null to disable. Pass {} for provider defaults.

thinking is a per-turn parameter — pass it on every call where you want reasoning enabled.

import asyncio
from backboard import BackboardClient

async def main():
    client = BackboardClient(api_key="YOUR_API_KEY")

    response = await client.send_message(
        "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
        llm_provider="openai",
        model_name="o3",
        thinking={"effort": "high"},
        stream=False
    )
    print(response.reasoning)
    print(response.content)

if __name__ == "__main__":
    asyncio.run(main())

​
Fields

FieldTypeDescription
effortstringReasoning depth: low, medium, high, or max
budget_tokensintegerMax tokens for internal reasoning
max_tokensintegerMax tokens for reasoning output
exclude_reasoningbooleanOmit reasoning traces from response

​
Provider Support

ProviderSupported FieldsExample
OpenAIeffort{"effort": "high"}
Anthropiceffort, budget_tokens{"effort": "high"}
AWS Bedrockeffort, budget_tokens{"effort": "high"}
Google (Gemini 2.5)budget_tokens{"budget_tokens": 8192}
Google (Gemini 3)effort{"effort": "high"}
xAIeffort{"effort": "high"}
OpenRoutermax_tokens, exclude_reasoning{"max_tokens": 4096}
Cerebrasmax_tokens, exclude_reasoning{"max_tokens": 4096}

​
Provider Details

OpenAI: Models: o1, o3, o4, gpt-5 series. Default effort: medium.

Anthropic: Claude Opus 4.7 and newer (including Opus 5 and Fable 5) use adaptive thinking — pass effort (low, medium, high, max); budget_tokens is not accepted for these models. Older models (Claude 3.7 Sonnet, Sonnet 4, Opus 4–4.6) use budget_tokens (min 1,024). Backboard routes to the correct mode automatically.

AWS Bedrock: Bedrock-hosted Anthropic Claude models. Claude Opus 4.7 and newer (including Opus 5 and Fable 5) use adaptive thinking — pass effort; budget_tokens is rejected for these models. Older Bedrock Claude models accept budget_tokens (min 1,024) or effort. Backboard routes to the correct mode automatically.

Google (Gemini 2.5): Max budget: 32,768 (Pro) / 24,576 (others).

Google (Gemini 3): Uses effort (low through max). Thinking is on by default.

xAI: Reasoning models — grok-4.3 and its aliases grok-latest, grok-3*, grok-4*, grok-4-fast-reasoning, grok-4-1-fast-reasoning, grok-3-mini* — accept effort (low, medium, high, max); max maps to xAI’s highest level. Omit thinking to avoid setting a Backboard control (xAI’s own default is low); pass {} for Backboard’s default medium. The *-non-reasoning variants do not support effort.

OpenRouter / Cerebras: Use max_tokens (int > 0) and exclude_reasoning (bool).

Best for complex tasks: math, code generation, multi-step reasoning, analysis.

Thinking uses extra tokens and adds latency. Skip it for simple questions.

​
Non-Streaming Examples

​
OpenAI

  • Python

  • JavaScript

  • TypeScript

import asyncio
from backboard import BackboardClient

async def main():
    client = BackboardClient(api_key="YOUR_API_KEY")

    response = await client.send_message(
        "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
        llm_provider="openai",
        model_name="o3",
        thinking={"effort": "high"},
        stream=False
    )
    print(response.reasoning)
    print(response.content)

if __name__ == "__main__":
    asyncio.run(main())

​
Anthropic

Use effort for Claude Opus 4.7+ (adaptive thinking). Use budget_tokens for older models.

  • Python

  • JavaScript

  • TypeScript

# Claude Opus 4.7+ — use effort
response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="anthropic",
    model_name="claude-opus-4-7-20250501",
    thinking={"effort": "high"},
    stream=False
)
print(response.reasoning)
print(response.content)

​
AWS Bedrock

Use effort for Claude Opus 4.7+ on Bedrock. Use budget_tokens for older Bedrock Claude models.

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="aws-bedrock",
    model_name="anthropic.claude-opus-4-7",
    thinking={"effort": "high"},
    stream=False
)
print(response.reasoning)
print(response.content)

​
Google (Gemini 2.5)

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="google",
    model_name="gemini-2.5-pro",
    thinking={"budget_tokens": 8192},
    stream=False
)
print(response.reasoning)
print(response.content)

​
Google (Gemini 3)

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="google",
    model_name="gemini-3.1-pro-preview",
    thinking={"effort": "high"},
    stream=False
)
print(response.reasoning)
print(response.content)

​
xAI

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="xai",
    model_name="grok-4.3",
    thinking={"effort": "high"},
    stream=False
)
print(response.reasoning)
print(response.content)

​
OpenRouter

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="openrouter",
    model_name="minimax/minimax-m1",
    thinking={"max_tokens": 4096},
    stream=False
)
print(response.reasoning)
print(response.content)

​
Cerebras

  • Python

  • JavaScript

  • TypeScript

response = await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="cerebras",
    model_name="z-ai/glm-4.7",
    thinking={},
    stream=False
)
print(response.reasoning)
print(response.content)

​
Streaming Examples

Same as above but with stream=True. The model thinks first, then streams the response.

​
OpenAI

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="openai",
    model_name="o3",
    thinking={"effort": "high"},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Anthropic

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="anthropic",
    model_name="claude-opus-4-7-20250501",
    thinking={"effort": "high"},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
AWS Bedrock

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="aws-bedrock",
    model_name="anthropic.claude-opus-4-7",
    thinking={"effort": "high"},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Google (Gemini 2.5)

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="google",
    model_name="gemini-2.5-pro",
    thinking={"budget_tokens": 8192},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Google (Gemini 3)

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="google",
    model_name="gemini-3.1-pro-preview",
    thinking={"effort": "high"},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
xAI

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="xai",
    model_name="grok-4.3",
    thinking={"effort": "high"},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
OpenRouter

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="openrouter",
    model_name="minimax/minimax-m1",
    thinking={"max_tokens": 4096},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Cerebras

  • Python

  • JavaScript

  • TypeScript

async for chunk in await client.send_message(
    "A driver was going the wrong way down a one-way street. He passed several police officers, but they did not stop him. Why? Think step by step.",
    llm_provider="cerebras",
    model_name="z-ai/glm-4.7",
    thinking={},
    stream=True
):
    t = chunk.get("type")
    if t == "reasoning_streaming":
        print(chunk.get("content", ""), end="", flush=True)
    elif t == "reasoning_ended":
        print("\n---------Reasoning Ended, Response Started---------")
    elif t == "content_streaming":
        print(chunk.get("content", ""), end="", flush=True)

​
Check Model Support

models = await client.list_models(supports_thinking=True, model_type="llm")
for m in models:
    print(f"{m.provider}/{m.name}")

See Models for details.