Many Workers AI models write reasoning text before the answer. Reasoning uses tokens, time and neurons. Use reasoning_effort to limit it.
Set reasoning_effort
Cloudflare accepts the OpenAI reasoning_effort field on /compat. Laravel AI does not send it. The package does.
If you do not set it, the model chooses its own effort. Measured on 2026-09-09 on one account, with @cf/openai/gpt-oss-120b and one two-sentence question:
reasoning_effort |
Reasoning | Answer | Wall time | Neurons |
|---|---|---|---|---|
low |
20 chars | 363 chars | 1.4 s | 9.6 |
| unset | 104 chars | 433 chars | 2.6 s | 11.3 |
high |
1,564 chars | 296 chars | 7.4 s | 31.4 |
The low answer was not worse. These figures show the order of magnitude, not a guarantee. low is a good default for latency-sensitive work.
Valid values are low, medium and high. Cloudflare returns HTTP 400 for OpenAI none and minimal. The package rejects them before the request, with an InvalidArgumentException.
Set it for all agents
'workers-ai' => [
// ...
'reasoning_effort' => 'low',
],Set it for one agent
use Meirdick\WorkersAi\Attributes\ReasoningEffort;
#[ReasoningEffort(ReasoningEffort::LOW)]
class DraftingAgent implements Agent { /* ... */ }Order of precedence
From lowest to highest:
reasoning_effortin the provider config.#[ReasoningEffort]on the agent.- A
reasoning_effortkey returned byproviderOptions().
Which models obey it
| Model | Result |
|---|---|
@cf/openai/gpt-oss-120b |
Obeys all three levels. |
@cf/zai-org/glm-5.3-flash |
Any value turns reasoning off. |
@cf/zai-org/glm-4.7-flash |
Ignores it. |
Test your own model before you rely on these results.
The thinking flag
chat_template_kwargs.thinking is an older control that depends on the model. It works on the Kimi chat template and on models that share it. It had no effect on @cf/zai-org/glm-5.3-flash. Try reasoning_effort first.
To use it, return it from providerOptions():
use Laravel\Ai\Enums\Lab;
// Inside your agent class:
public function providerOptions(Lab|string $provider): array
{
return $provider === 'workers-ai'
? ['chat_template_kwargs' => ['thinking' => false]]
: [];
}When thinking is true and the token cap is below 2048, the package raises the cap to 2048 and writes a warning to the log. Reasoning and the answer share one cap, and a small cap leaves no room for the answer.
Empty answers
A reasoning model can use its whole token cap on thinking. Cloudflare then returns HTTP 200 with content: null, and Laravel AI shows an empty string. Your code cannot tell this from a real empty answer.
So the package throws EmptyResponseException. This is on by default. A tool-call turn with no text does not throw.
To receive the empty response instead:
'workers-ai' => [
// ...
'throw_on_empty_response' => false,
],When you see this exception, raise the token cap or lower reasoning_effort.
Cut-off answers
Cloudflare reports a cut-off answer with finish_reason: "length". Laravel AI returns the partial text as a success. To get an exception instead, turn on throw_on_truncation:
'workers-ai' => [
// ...
'throw_on_truncation' => true,
],The package then throws TruncatedResponseException on any step that ends with finish reason length. Use this for extraction and drafting, where a partial answer is worse than none.
Where reasoning text appears
| Model | Reasoning key in the response |
|---|---|
@cf/zai-org/glm-5.3-flash |
reasoning_content |
@cf/openai/gpt-oss-120b |
reasoning and reasoning_content |
@cf/zai-org/glm-4.7-flash |
reasoning and reasoning_content |
@cf/meta/llama-3.3-70b-instruct-fp8-fast |
reasoning, always null |
@cf/qwen/qwq-32b |
None. Reasoning is inside content. |
The package reads all of these keys. On laravel/ai 1.x, it also fills StepResponse::$reasoning. On both laravel/ai lines, it sends reasoning text back between tool-call turns. You do not handle this.