Reasoning
Modern frontier models can “think” before they answer: they spend tokens working through a problem privately before committing to a response. This tends to produce better answers on hard problems (math, code, multi-step planning), at the cost of extra latency and output tokens. Providers expose this in different ways and under different names – reasoning, thinking, thoughts – but chatlas gives you one consistent interface to enable it, see it, and program against it.
Enabling reasoning
Reasoning-capable providers accept a reasoning parameter when you construct the chat client. The simplest option is a string effort level, which works the same way across providers:
import chatlas as ctl
chat = ctl.ChatAnthropic(reasoning="medium")
chat.chat("What's the average airspeed velocity of an unladen swallow?")chat = ctl.ChatOpenAI(reasoning="medium")
chat = ctl.ChatGoogle(reasoning="medium")"low", "medium", and "high" are accepted everywhere; individual providers offer more (e.g., ChatAnthropic() goes up to "max", and ChatGoogle() down to "minimal"). Your IDE will autocomplete the levels available for the provider you’re using, and each provider’s reference page documents its full set. ChatAzureOpenAI() and ChatBedrockAnthropic() take reasoning too (Bedrock accepts a token budget rather than an effort level).
If you’d rather control the budget directly, ChatAnthropic() and ChatGoogle() also accept an integer number of tokens to allocate to reasoning:
chat = ctl.ChatAnthropic(reasoning=2048)And for full control, pass the provider’s native configuration type:
chat = ctl.ChatOpenAI(reasoning={"effort": "high", "summary": "detailed"})Many open-weight models (e.g., DeepSeek R1, Qwen QwQ) decide for themselves when to think – there’s no parameter to set. ChatDeepSeek(), ChatOllama(), and ChatOpenAICompletions() (LM Studio, vLLM, etc.) capture that reasoning automatically.
Seeing reasoning
When reasoning is enabled, .chat() shows it to you as it streams. At the console, reasoning appears in a Thinking panel ahead of the response:
╭─ Thinking ───────────────────────────────────────────────╮
│ Let me work through this. 2+2 is a basic addition │
│ problem, so the answer is 4. │
╰──────────────────────────────────────────────────────────╯
The answer is **4**.
Reasoning can get long, so the panel is capped: it keeps the most recent lines (10 by default) and the title notes how many earlier lines scrolled away (e.g., Thinking (… 12 earlier lines)). Tune this with .set_echo_options() – pass thinking_max_lines=None to keep it all.
In a notebook, reasoning renders as a collapsible “Thinking” block instead: open while the model is thinking, collapsed once the response arrives, so it never crowds out the answer.
How much you see is controlled by the echo parameter. The default (echo="output") includes reasoning and tool calls; echo="text" shows just the model’s answer; echo="none" shows nothing at all.
Accessing reasoning programmatically
For text, reasoning stays out of your way: str() on a .chat() result and the chunks from .stream() contain only the model’s answer. To consume reasoning yourself – say, to render it in a chatbot UI – set content="all" and the stream yields rich content objects as they’re generated:
stream = chat.stream("What is 2+2?", content="all")
for chunk in stream:
print(type(chunk))<class 'chatlas._content.ContentThinkingDelta'>
<class 'chatlas._content.ContentThinkingDelta'>
<class 'str'>
<class 'str'>
Each ContentThinkingDelta carries a fragment of reasoning text (.thinking), plus a .phase ("start", "body", or "end") marking where one thinking block begins and ends – handy for opening and closing a collapsible region in your UI. This is the same pattern used for displaying tool calls in bespoke apps.
Once the response completes, the reasoning is stored on the assistant turn as ContentThinking:
turn = chat.get_last_turn()
print([type(c).__name__ for c in turn.contents])['ContentThinking', 'ContentText']
Reasoning across turns
You generally don’t need to think about what happens to reasoning on the next request – chatlas handles each provider’s requirements for you:
- Anthropic returns its full thinking and requires it to be replayed (with cryptographic signatures) for subsequent requests, which chatlas does automatically.
- OpenAI keeps raw reasoning private and returns only a summary. chatlas also requests the encrypted reasoning and sends it back on your behalf, so multi-turn reasoning works without OpenAI-side state.
- OpenAI-compatible backends vary: most expect prior reasoning to be dropped from the conversation, which is what
ChatOpenAICompletions()does by default (the reasoning stays on the turn – it’s just not sent back). Passpreserve_thinking=Truefor backends that want it replayed, asChatDeepSeek()does.
One thing to keep in mind: reasoning tokens are billed as output tokens, so higher effort levels cost more. See monitoring for keeping an eye on token usage and cost.