feat(dte): dynamic thinking effort — full series trial (PRs 1-5 + e2e addenda merged) - #29
Open
easonLiangWorldedtech wants to merge 81 commits into
Open
feat(dte): dynamic thinking effort — full series trial (PRs 1-5 + e2e addenda merged)#29easonLiangWorldedtech wants to merge 81 commits into
easonLiangWorldedtech wants to merge 81 commits into
Conversation
…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.
…/dte-3-native-tool
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)
…e, in-chat display
…tadata in say-rejection test
7 tasks
… display strings (18 locales)
…oggle; default-source label
… display strings (18 locales; trial-local, mirrors dte-4 e298f26)
…i18n emoji/space, toggle value+hover, default-source label, baselines)
The composer toggle is reachable with no task open, but the setTaskThinkingEffort handler was a silent no-op in that state (task-gated). Park the enum-validated selection as a provider-level pending effort; createTask consumes it on the next top-level task, re-checking the new task model capability before applying (source you, single in-chat line, same as the task-local path). The webview state falls back to the pending value so the toggle keeps showing the selection until the task consumes or discards it (unsupported levels are consumed, not leaked).
The menu content inherited the base Popover p-4 (16px) which made the small option list feel airy. Override it with p-1.5 and tighten the header/hint paddings; option rows keep their existing py-1.
…adding # Conflicts: # src/core/webview/ClineProvider.ts
… effort levels (F7)
The menu renders through a portal as a sibling of the story container (opened below the trigger), so capturing the story element never included the menu. Capture the menu element itself and finish the portal entrance animations before the snapshot so the menu-state baseline actually covers the menu content.
…act padding) The menu-state baselines now capture the portal menu element itself (previously the story container was captured, which excluded the menu) and reflect the compacted p-1.5 content padding. Resting-state baselines are unchanged.
Adds a second DTE e2e suite that drives one task through a scripted switching sequence (baseline -> applied -> no-op -> applied -> oscillation refusal) against openai/gpt-5.1, asserting the per-request OpenRouter reasoning envelope plus the display says and tool results. Extracts the shared OpenRouter capture proxy from thinking-effort-tool.test.ts into thinking-effort-proxy.ts and switches that suite's request lookups to raw-body tool-call-id matching. Fixtures are scoped by model + hasToolResult + unique turnIndex because aimock's toolCallId matcher only inspects the last message and post-tool requests end with a fresh user message.
The toolCallId matcher only inspects the last message of the request, but post-tool requests now end with a fresh user env-details message, so the old thinking-effort-tool fixture could never match (aimock 404 -> 30s e2e-mock timeout). This merge brings the turnIndex-scoped aimock fixtures, the shared OpenRouter capture proxy, and the new effort-switching suite; the 5 conflicting locales are resolved as translated name/description (e2e branch) plus the F7 supportedReasoningEfforts hint (HEAD).
CodeRabbit pre-merge check on the addendum (docstring coverage 14.29% < 80%, 7 functions across 3 files): add JSDoc to the five internal proxy helpers and firstRequestCarrying so every function touched by this diff is self-documenting (withOpenRouterCaptureProxy was already documented).
… event race) CI e2e-mock failed 2 !== 3 on "exactly three thinkingEffort display says": the final display say is observed on the Message channel after the TaskCompleted event resolved waitUntilCompleted (separate event channels, no cross-channel ordering guarantee; under CI load the queue lags by more than one turn). Await the expected says with a bounded settle (5s, 100ms) before detaching the listener: a genuine shortfall still fails the same assertion, the race no longer does.
…l-in CodeRabbit docstring-coverage pre-merge check on the stacked diff flags the six provider getModel() overrides this PR touches (base-openai-compatible, friendli, openai, lm-studio, native-ollama, router-provider). Document each with the F7 fill-in-the-gap semantic so every function introduced or touched by this PR's own delta is self-documenting.
Follow-up to the CodeRabbit docstring-coverage pre-merge check. Document the functions introduced or touched by this PR's stacked diff that still lacked JSDoc: - SetThinkingEffortTool: effortRank, getGuardState, execute, handlePartial - filter-tools-for-mode: applyModelToolCustomization (its doc block was orphaned by an intervening interface; moved it directly above the function) - router-provider: supportsTemperature (line re-touched by the F7 diff) Comments only; no behavior change.
Follow-up to the 2026-08-24 CodeRabbit full review of this stacked PR. Four major findings, three fixed, one documented as design: 1. new_task schema strict-mode violation: thinking_effort was in properties but not required, which the Anthropic API rejects under strict: true + additionalProperties: false (the whole tool definition fails). It now uses the same ["string", "null"] + required pattern as todos; null is the omitted-value sentinel the tool treats as absent (unit-tested). 2. NewTaskTool: the invalid thinking_effort path now advances the consecutive-mistake guardrail and records the tool error like every other failure path, so a model repeating an unsupported effort trips the mistake loop (unit-tested). 3. E2E suite teardown: the new_task suite switches the profile to the Anthropic provider with an ephemeral proxy base URL and sets the global reasoning-effort fields; the teardown now explicitly clears them (anthropicBaseUrl, apiModelId, enableReasoningEffort, reasoningEffort) so a later suite selecting the anthropic provider is not pointed at the closed local port and does not inherit this suite's effort baseline. 4. Task-local effort on OpenAI-compatible providers: documented in the PR discussion rather than changed - setRuntimeThinkingEffort rewrites the per-task apiConfiguration and rebuilds the API handler, so provider requests are built from the task-local config (the switching e2e asserts the wire envelope changes on the request after an applied change); the metadata.reasoningEffort per-request override is the PR-2 Anthropic channel, and this PR's design deliberately keeps existing wire emit unchanged (plan section 12.1, user-confirmed caveat that some local servers ignore the parameter). Type surfaces: NativeToolArgs.new_task.thinking_effort and ToolUse.params now admit the null sentinel. tsc clean, eslint clean, 55 unit tests + 5 DTE e2e suites passing locally.
A task reopened from history constructed a fresh Task with no runtime thinking effort, so the displayed and effective effort silently fell back to the settings value even when the task had a per-task override. - historyItemSchema: optional thinkingEffort + thinkingEffortSource - taskMetadata: accepts and spreads both (only when active) - Task.saveClineMessages: writes the active override via getRuntimeThinkingEffort() - Task ctor (historyItem branch): restores it via setRuntimeThinkingEffort, which also merges into the in-memory apiConfiguration copy and rebuilds the handler - spec: 4 new persistence round-trip cases (restore, no-op without effort, save writes effort, save omits effort while inactive)
packages/types compiles with node16 module resolution, where relative import specifiers need an explicit file extension (./model.js).
OpenAI-compatible (self-hosted) profiles never declared a reasoning effort capability, so the dynamic thinking effort surfaces stayed hidden and the per-request effort envelope was not offered: - add a SupportedEffortLevels declaration control to the OpenAI-compatible provider settings (bound through setApiConfigurationField, persisted on Save); an empty declaration unchecks the Enable Reasoning Effort switch so UI and wire state cannot drift; - ThinkingBudget options now derive from the custom-model entry's own capability, else the declared levels, else the default set; - resolveReasoningEffortCapability synthesizes a minimal ModelInfo from the declaration when no model info reaches the webview at all; - new i18n keys in all 18 locales (parity-checked).
A custom model's own capability array may include the "disable" level (e.g. ollama think/no-think style models). Selecting it previously stored reasoningEffort="disable" while enableReasoningEffort stayed true, so the parent switch showed reasoning as enabled although the selected effort disables it. The effort binding now clears the stored model effort and unchecks the switch — the same end state as unchecking it by hand. CodeRabbit finding (functional correctness, minor) on PR Zoo-Code-Org#1366.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Trial branch: all DTE (dynamic thinking effort) branches composed into ONE
This branch merges the entire DTE 5-PR series + both e2e addenda so it can be tried out as a single build. It is NOT a submission PR - the real PRs are on Zoo-Code-Org/Zoo-Code:
Merge history (all additive, no rebase)
19954d3 (dte-3 head) -> merge dte-4 (3ae54d1; 17 locale i18n conflicts resolved as union: dte-3 translations + dte-4 new keys) -> merge dte-5 (5b33232; 2 conflicts in vscode-extension-host.ts resolved keep-both) -> merge dte-5-e2e (ce0e90b) -> merge dte-3-e2e (f152b98).
Verified
How to try it
Base: main @ 78c712a (same as upstream main at branch time).