Skip to content

feat(settings): let OpenAI-compatible profiles declare supported reasoning effort levels (DTE F7) - #1366

Open
easonLiangWorldedtech wants to merge 68 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:feat/dte-7-f7-openai-compat
Open

feat(settings): let OpenAI-compatible profiles declare supported reasoning effort levels (DTE F7)#1366
easonLiangWorldedtech wants to merge 68 commits into
Zoo-Code-Org:mainfrom
easonLiangWorldedtech:feat/dte-7-f7-openai-compat

Conversation

@easonLiangWorldedtech

@easonLiangWorldedtech easonLiangWorldedtech commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Closes #1376

What & why

Self-hosted / OpenAI-compatible models (custom OpenAI endpoint, LM Studio, Ollama, and similar) do not advertise supportsReasoningEffort in the model registry, so the dynamic thinking effort (DTE) features — set_thinking_effort tool, composer ThinkingEffortToggle, TaskHeader chip, new_task thinking_effort param — were silently disabled for them even when the underlying model supports effort levels (e.g. Qwen3 via LM Studio).

F7 lets the user declare the effort levels their model supports, per API profile, via a new provider setting.

Design (conservative, additive)

  • New provider setting (packages/types/src/provider-settings/common.ts): supportedReasoningEfforts: z.array(reasoningEffortExtendedSchema).optional() on baseProviderSettingsShape — a per-profile declaration of canonical effort levels (canonical enum only; the UI-level "disable" sentinel cannot be declared).

  • Single resolution semantic, applied on both sides: when the resolved ModelInfo does NOT have supportsReasoningEffort (undefined) AND the profile declares a non-empty supportedReasoningEfforts, the model is treated as supporting exactly that array. Registry values are NEVER overridden (fill-in-the-gap only), so registry models (deepseek, openrouter, gemini, friendli catalogs, etc.) are untouched.

  • Extension side: small shared helper withDeclaredReasoningEffort(modelInfo, settings) in src/api/model-capabilities.ts, applied at the construction sites where OpenAI-compatible ModelInfo reaches consumers via getModel():

    • BaseOpenAiCompatibleProvider.getModel() (covers friendli/fireworks/baseten/sambanova/zai)
    • FriendliHandler.getModel() (custom override)
    • OpenAiHandler.getModel() (custom OpenAI endpoint, incl. DeepSeek/Kimi/Moonshot/Mimo subclasses)
    • LmStudioHandler.getModel()
    • NativeOllamaHandler.getModel()
    • RouterProvider.getModel() (covers LiteLLM, kenari, nanogpt, opencode-go, vercel-ai-gateway, zoo-gateway)

    All downstream DTE consumers (task.api.getModel().info in SetThinkingEffortTool, NewTaskTool, filter-tools-for-mode, webviewMessageHandler.setTaskThinkingEffort, request transform capability gates in shared/api.ts / api/transform/reasoning.ts) pick the capability up automatically — no request transforms changed; the existing capability gate already controls the reasoning_effort / OpenRouter reasoning envelope emit once capability is present.

  • Webview side: mirror helper resolveReasoningEffortCapability(model, apiConfiguration) in webview-ui/src/utils/thinkingEffort.ts, applied inside computeThinkingEffortDisplay so the composer toggle and TaskHeader chip render for declared models (both surfaces read the webview state ModelInfo, which has no extension-side info).

  • Experiments-screen hint (ExperimentalSettings.tsx): a small info line under the Dynamic thinking effort row noting that OpenAI-compatible profiles can declare supported effort levels per profile; the declared effort is sent per request but the local server may ignore the parameter.

  • i18n: new key settings:experimental.DYNAMIC_THINKING_EFFORT.hint added to all 18 locales (tab-indented JSON, CRLF preserved, node scripts/find-missing-translations.js passes).

Tests (all passing)

  • packages/types (provider-settings.test.ts): schema accepts canonical declarations across provider branches, rejects non-canonical values ("turbo") and the "disable" sentinel, accepts empty/omitted. Full types suite: 382 passed.
  • Extension unit (src/api/__tests__/model-capabilities.spec.ts): registry-wins (array + boolean), fill-in, empty/undefined declaration = no-op, no shared mutation.
  • Extension handler-level (src/api/providers/__tests__/f7-declared-reasoning-effort.spec.ts): fill-in visible at getModel() for OpenAiHandler (sane defaults + custom model info), LmStudio, NativeOllama (via public fetchModel()), BaseOpenAiCompatibleProvider subclasses, and RouterProvider fallback (LiteLLM); registry-wins and no-declaration no-ops.
  • Webview (webview-ui/src/utils/__tests__/thinkingEffort.spec.ts): undefined capability + supportedReasoningEfforts: ['low','high','max'] resolves with those levels; registry value wins over declaration; no declaration = null; disable-sentinel exclusion preserved.
  • Regression (touched paths): base-openai-compatible, openai, lm-studio, lite-llm, friendli, zai, fireworks, baseten, sambanova, native-ollama, webviewMessageHandler thinking-effort, setThinkingEffortTool, newTaskThinkingEffort, filter-thinking-effort, filter-tools-for-mode, shared api — all green.
  • Typecheck/lint: pnpm --filter @roo-code/vscode-webview exec tsc and root pnpm check-types (turbo) both exit 0; eslint clean on changed files; src/eslint-suppressions.json unchanged.

Branch note (addendum pattern)

Per the established DTE addendum-PR pattern (same as feat/dte-5-e2e / feat/dte-3-e2e), this branch is cut from the composed DTE head 9b6c8bc9f (feat/dte-trial-all), so the diff vs upstream main includes the whole 7-PR DTE series. The new F7 delta vs 9b6c8bc9f is 32 files: +621/−39 lines.

 git diff --stat 9b6c8bc9f  (staged F7 delta)
 32 files changed, 621 insertions(+), 39 deletions(-)

Related

  • Upstream DTE tracking issue: #1330 (this PR is the F7 addendum: OpenAI-compatible / self-hosted profiles declaring reasoning-effort levels — requested during DTE series user testing).
  • Stacks on the DTE series: #1336#1338#1354#1355#1356#1359; e2e addendum #1361.

Checklist

  • New/changed provider setting (supportedReasoningEfforts) round-trips: types schema → extension → webview state
  • Registry-wins / fill-in-only semantics verified (unit, handler-level, and webview specs)
  • i18n: experimental-settings hint in all 18 locales
  • Tests: 17 F7 specs + webview 45 + regression suites green; tsc + eslint clean; src/eslint-suppressions.json unchanged
  • e2e-mock green (DTE suites 5/5, incl. the mid-task and switching addenda)
  • Standalone F7 delta ≤ 1000 lines (32 files, +621/−39 vs the stacked base)

Documentation impact

No user-facing docs required: the feature is gated behind the DYNAMIC_THINKING_EFFORT experiment, and an in-app hint under the DTE row of the experimental settings documents the new provider setting (localized, 18 locales).

Summary by CodeRabbit

  • New Features
    • Added experimental dynamic thinking-effort controls for supported models.
    • Users can adjust effort during tasks and delegated-task approvals.
    • Added task-header indicators and chat messages for effort, source, reasons, and refusals.
    • Added supported-effort configuration for compatible custom model profiles.
    • Improved effort inheritance for delegated tasks and provider requests.
  • Bug Fixes
    • Added safeguards for unsupported levels, oscillation, and excessive escalations.
  • Localization
    • Added translated thinking-effort labels, guidance, and status messages.
  • Tests
    • Added comprehensive unit, integration, end-to-end, and visual coverage.

…nd adaptive effort envelope

DTE series 2/5 (part of Zoo-Code-Org#1329).

- ApiHandlerCreateMessageMetadata.reasoningEffort: per-request override channel
- resolveEffectiveReasoningEffort: single shared resolution point (override > settings > model default)
- AnthropicHandler: adaptive output_config.effort envelope in both requestParams branches (in-range only)
- Task: setRuntimeThinkingEffort/getRuntimeThinkingEffort with in-memory apiConfiguration merge/restore, per-request metadata at all four createMessage sites, dispose() reset; never persisted
DTE series 2/5 — addresses the CodeRabbit review finding on Zoo-Code-Org#1338:
when a task-local thinking-effort override is active, updateApiConfiguration()
now re-captures the incoming profile's reasoningEffort as the restore value
and re-applies the override on top of the new in-memory copy, so clearing the
override restores the NEW profile value instead of the stale one. Additive:
activation and clearing semantics are otherwise unchanged.

Adds two regression tests (override active + profile switch restores new
value; inactive updateApiConfiguration unchanged behavior).
DTE series 2/5 — addresses the CodeRabbit docstring-coverage warning on Zoo-Code-Org#1338
(33.33% < 80% across the functions touched by the diff):
- AnthropicHandler.createMessage: documents the shared effective-effort
  resolution and the adaptive output_config.effort envelope (in-range only).
- Task.dispose: documents centralized teardown incl. the transient task-local
  override reset.
- Task.updateApiConfiguration: documents the override-preservation behavior
  (re-captured restore value + re-applied override on the new in-memory copy).

Comment-only change: 30/30 patch lines and 10/10 branches unchanged;
317/317 tests and tsc --noEmit re-verified green.
Add the set_thinking_effort native tool (DTE series 3/5): the model adjusts
its own per-turn thinking effort mid-task with no approval gate.

- Guardrails: one-line chat notification (success or refusal), escalation cap
  (max 3 upward changes per task), A->B->A oscillation refusal, hard clamp to
  the model capability array (ties toward the lower level).
- Gating: dynamicThinkingEffort experiment + model supportsReasoningEffort
  (non-empty array or true), evaluated at task start so the tool list stays
  stable within a task (prompt-cache safety).
- Display: webview ChatRow one-line row (applied / oscillation / escalation
  refusal), i18n keys in all 17 locales; partial streaming updates the same
  line.
- Tests: executor (clamp/cap/oscillation/no-op/no-approval/display), parser
  (partial + complete), dispatch, gating matrix, schema wiring, ChatRow
  display.

Stacked on DTE PR-1 (experiment flag) and PR-2 (task-local runtime effort
state). Closes Zoo-Code-Org#1330.
Address PR review feedback on set_thinking_effort (DTE series 3/5):

- Executor: seed the per-task guard history with the task's effective
  baseline so returning from a changed value to the original baseline is
  refused as oscillation (A -> B -> A); existing no-op behavior preserved.
- Parser: only build nativeArgs when effort AND reason are strings; a
  non-string payload now fails at parse time and cannot reach the executor.
- Gating: a supportsReasoningEffort array that only lists 'disable' no
  longer exposes the tool (it could apply no level).
- i18n: translate the new thinkingEffort chat strings into all 17
  non-English webview locales (placeholders preserved).
- Tests: regression tests for each change plus branch-coverage for the
  previously partial lines (non-string args, 'disable'-only capability,
  baseline oscillation, partial streaming without params, description
  fallback, capability robustness). All touched patch lines are now
  fully branch-covered (codecov patch partials resolved).

CodeRabbit: Zoo-Code-Org#1354
… post-mode-switch revalidation, ask prefill normalization)
@easonLiangWorldedtech

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@easonLiangWorldedtech easonLiangWorldedtech changed the title feat(dte): let OpenAI-compatible profiles declare supported reasoning effort levels (F7) feat(settings): let OpenAI-compatible profiles declare supported reasoning effort levels (DTE F7) Aug 26, 2026
The new supportedReasoningEfforts hint in the nl locale said
"OpenAI-compatibiele"; the correct spelling is "OpenAI-compatibele".
(CodeRabbit on trial PR 1379)
…a think param

getOllamaThinkParam() only read this.options.reasoningEffort, so a
task-local per-request override (metadata.reasoningEffort) was ignored by
the native Ollama provider while every other provider resolves it via the
shared override-first order. Thread the override through
buildChatRequestOptions() into getOllamaThinkParam() (override ??
settings fallback); completePrompt keeps the settings-derived value since
it has no per-request metadata. Two new provider request tests cover the
override-beats-settings and override-without-settings paths. (CodeRabbit
on trial PR 1379)
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch and removed awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 29, 2026
@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Review process

Thanks for contributing. This comment tracks the review sequence and the next action.

  1. Required CI checks pass.
  2. The workflow starts CodeRabbit automatically.
  3. For eligible human-authored PRs, CodeRabbit reviews and approves the latest commit.
  4. A human maintainer reviews and approves after CodeRabbit.

Current step: Required CI passed. Wait for CodeRabbit to approve the latest commit.

@github-actions github-actions Bot added awaiting-review PR changes are ready and waiting for maintainer re-review has-conflicts PR has merge conflicts with the base branch and removed has-conflicts PR has merge conflicts with the base branch awaiting-review PR changes are ready and waiting for maintainer re-review labels Aug 29, 2026
@github-actions github-actions Bot added has-conflicts PR has merge conflicts with the base branch coderabbit-review-active Required CI passed; CodeRabbit review is active awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit and removed awaiting-review PR changes are ready and waiting for maintainer re-review has-conflicts PR has merge conflicts with the base branch coderabbit-review-active Required CI passed; CodeRabbit review is active awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit labels Aug 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

awaiting-coderabbit Waiting for CodeRabbit to approve the latest commit coderabbit-review-active Required CI passed; CodeRabbit review is active

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(settings): let OpenAI-compatible profiles declare supported reasoning effort levels (DTE F7)

2 participants