Reasoning-capable models have been usable in BoxLang AI for a while. params passes straight through to the provider body, so params: { thinking: { type: "enabled", budget_tokens: 10000 } } for Claude, or params: { reasoning_effort: "high" } for OpenAI, already reached the API. What never worked was reading the reasoning back. It was parsed out on arrival and silently dropped. In 3.4.0, that's fixed, and it's fixed the same way for every provider.
One key, every provider
Reasoning now surfaces on the same OpenAI-shaped envelope every provider already normalizes onto: choices[].message.reasoning synchronously, choices[].delta.reasoning while streaming.
// Enable extended thinking on Claude
result = aiChat( "Solve this step by step: ...", params: {
thinking: { type: "enabled", budget_tokens: 10000 }
}, options: { returnFormat: "raw" } )
reasoning = result.choices[1].message.reasoning ?: "" // "" when the model didn't think, absence is normal
answer = result.choices[1].message.content
// OpenAI reasoning effort
result = aiChat( "...", params: { reasoning_effort: "high" }, provider: "openai" )
Streaming works the same way, reasoning arrives on delta.reasoning, ahead of delta.content:
aiChatStream( "...", ( chunk ) => {
if ( !isNull( chunk.choices?.first()?.delta?.reasoning ) ) {
print( chunk.choices.first().delta.reasoning ) // thinking, as it streams
}
} )
Why this needed real work per provider
Every provider spells reasoning differently on the wire. Anthropic sends thinking_delta. DeepSeek sends reasoning_content. Ollama nests it under message.thinking. Claude-on-Bedrock uses delta.thinking. Every one of those is now either inherited automatically from BaseService's normalization or mapped explicitly, so your code reads chunk.choices.first().delta.reasoning regardless of which model is actually behind the call. MockService can even script reasoning output for offline tests.
Absence is normal, not an error
A model or provider that doesn't reason simply omits the key. Always read it defensively:
reasoning = delta.reasoning ?: ""
Reasoning is deliberately not a declared capability the way chat or embeddings are, because it varies per model, not per provider. Sonnet reasons, Haiku doesn't. GPT-5 reasons, GPT-4o doesn't. There's no single flag on a provider that tells you.
Kept strictly out of memory
Reasoning is kept separate from content and is never persisted to agent memory. That's a deliberate design choice, not an oversight: replaying a model's private thinking back to it on the next turn, as if it had actually said that out loud, changes its behavior in ways you didn't ask for.
One thing to watch on OpenAI: tools and reasoning don't mix (yet)
OpenAI doesn't accept function tools alongside active reasoning on /v1/chat/completions, the endpoint this module speaks. This bites even if you never explicitly set reasoning_effort, because OpenAI's default model reasons by default:
Function tools with reasoning_effort are not supported for <model> in /v1/chat/completions.
To use function tools, use /v1/responses or set reasoning_effort to 'none'.
Until Responses API support lands, pick one of two escapes:
// Tools, no reasoning, name a non-reasoning model explicitly
aiChat( "How hot is it in KC?", params: { tools: [ tool ], model: "gpt-4o" } )
// Tools on a reasoning model, turn reasoning off for the call
aiChat( "How hot is it in KC?", params: { tools: [ tool ], reasoning_effort: "none" } )
Claude, for one, isn't affected, it accepts extended thinking and tools together on its single endpoint.
Why it matters
Reasoning traces are one of the most useful debugging tools an agent gives you, and until now BoxLang AI threw them away. Now you can log them, show them to a human in a HITL approval prompt, or just use them to understand why an agent made the call it made, without writing a single provider-specific branch to get there.
That closes out the series. Five features, one theme: agents you can trust, because a human can step in when it matters, and because what the model reads and writes is defended on both sides.
Docs: Reasoning
Add Your Comment