Skip to content

[Bug]: GenerateOptions.thinkingBudget is never applied to OpenAI API request #1697

@HuJiShuang

Description

@HuJiShuang

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

  1. 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();
  1. Send a complex prompt that triggers extended reasoning.

  2. 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:

  • temperaturerequest.setTemperature()
  • reasoningEffortrequest.setReasoningEffort()
  • topPrequest.setTopP()
  • maxCompletionTokensrequest.setMaxCompletionTokens()
  • parallelToolCallsrequest.setParallelToolCalls()
  • seedrequest.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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/core/modelModel providers and formattersbugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    Status
    In progress

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions