Description
GenerateOptions.thinkingBudget field is accepted by the builder and stored in the options object, but never mapped to the OpenAIRequest during OpenAIChatFormatter.applyOptions(). This means the thinking_budget parameter is silently dropped and never sent to the LLM API, making the field effectively dead code.
This causes serious issues when using thinking/reasoning models (e.g., Qwen3, DeepSeek-R1): the model thinks without any token budget constraint, exhausts all maxCompletionTokens on reasoning, and returns empty content — the user gets no reply.
Steps to Reproduce
- Configure
OpenAIChatModel with thinkingBudget:
OpenAIChatModel.builder()
.apiKey("xxx")
.modelName("qwen3.7-max")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.generateOptions(GenerateOptions.builder()
.maxCompletionTokens(8192)
.thinkingBudget(4096) // <-- this is silently ignored
.additionalBodyParams(Map.of("enable_thinking", true))
.build())
.build();
-
Send a complex prompt that triggers extended reasoning.
-
Observe: the actual HTTP request body does not contain thinking_budget. The model uses unlimited thinking tokens, exhausts the 8192 max_completion_tokens budget entirely on reasoning_content, and returns empty content.
Root Cause
In OpenAIChatFormatter.applyOptions() (line ~49-91), the method maps most GenerateOptions fields to OpenAIRequest setters:
- ✅
temperature → request.setTemperature()
- ✅
reasoningEffort → request.setReasoningEffort()
- ✅
topP → request.setTopP()
- ✅
maxCompletionTokens → request.setMaxCompletionTokens()
- ✅
parallelToolCalls → request.setParallelToolCalls()
- ✅
seed → request.setSeed()
- ❌
thinkingBudget → NOT HANDLED — silently dropped
Additionally, OpenAIRequest does not have a dedicated thinkingBudget / thinking_budget field with @JsonProperty annotation.
Expected Behavior
One of the following:
Option A (Preferred): Add thinking_budget support to OpenAIRequest and OpenAIChatFormatter:
// In OpenAIRequest:
@JsonProperty("thinking_budget")
private Integer thinkingBudget;
// In OpenAIChatFormatter.applyOptions():
Integer thinkingBudget = getOptionOrDefault(options, defaultOptions, GenerateOptions::getThinkingBudget);
if (thinkingBudget != null) {
request.setThinkingBudget(thinkingBudget);
}
Option B: If the field is not yet meant to be functional, add clear documentation or throw an UnsupportedOperationException to prevent silent failure.
Actual Behavior
thinkingBudget is silently dropped. No warning, no error. The API request is sent without any thinking budget constraint.
Impact
- Severity: High — Causes empty responses from thinking models, completely breaking the user experience.
- Affected models: Any OpenAI-compatible API that supports
thinking_budget (Alibaba Cloud Bailian/DashScope Qwen3 series, potentially Anthropic Claude with thinking.budget_tokens).
Workaround
Pass thinking_budget via additionalBodyParams instead:
GenerateOptions.builder()
// .thinkingBudget(4096) // <-- does NOT work
.additionalBodyParams(Map.of(
"enable_thinking", true,
"thinking_budget", 4096)) // <-- works, goes through extraParams
.build()
Environment
- agentscope version: 2.0.0-RC1
- Java version: 17+
- LLM provider: Alibaba Cloud Bailian (DashScope OpenAI-compatible API)
- Model: qwen3.7-max
Description
GenerateOptions.thinkingBudgetfield is accepted by the builder and stored in the options object, but never mapped to theOpenAIRequestduringOpenAIChatFormatter.applyOptions(). This means thethinking_budgetparameter is silently dropped and never sent to the LLM API, making the field effectively dead code.This causes serious issues when using thinking/reasoning models (e.g., Qwen3, DeepSeek-R1): the model thinks without any token budget constraint, exhausts all
maxCompletionTokenson reasoning, and returns emptycontent— the user gets no reply.Steps to Reproduce
OpenAIChatModelwiththinkingBudget:Send a complex prompt that triggers extended reasoning.
Observe: the actual HTTP request body does not contain
thinking_budget. The model uses unlimited thinking tokens, exhausts the 8192max_completion_tokensbudget entirely onreasoning_content, and returns emptycontent.Root Cause
In
OpenAIChatFormatter.applyOptions()(line ~49-91), the method maps mostGenerateOptionsfields toOpenAIRequestsetters:temperature→request.setTemperature()reasoningEffort→request.setReasoningEffort()topP→request.setTopP()maxCompletionTokens→request.setMaxCompletionTokens()parallelToolCalls→request.setParallelToolCalls()seed→request.setSeed()thinkingBudget→ NOT HANDLED — silently droppedAdditionally,
OpenAIRequestdoes not have a dedicatedthinkingBudget/thinking_budgetfield with@JsonPropertyannotation.Expected Behavior
One of the following:
Option A (Preferred): Add
thinking_budgetsupport toOpenAIRequestandOpenAIChatFormatter:Option B: If the field is not yet meant to be functional, add clear documentation or throw an
UnsupportedOperationExceptionto prevent silent failure.Actual Behavior
thinkingBudgetis silently dropped. No warning, no error. The API request is sent without any thinking budget constraint.Impact
thinking_budget(Alibaba Cloud Bailian/DashScope Qwen3 series, potentially Anthropic Claude withthinking.budget_tokens).Workaround
Pass
thinking_budgetviaadditionalBodyParamsinstead:Environment