Skip to content

feat: redesign thinking level mapping — relative 4-level model, data-driven provider adaptation #73

Description

@Cloud370

Motivation

kuku's thinking-level system has four problems today:

  1. Hardcoded mapping does not cover provider differences. ThinkLevel (off/low/medium/high) is mapped per provider with three hardcoded match tables (openai_compat.rs: low→low / medium→medium / high→xhigh; openai_responses.rs: same; anthropic.rs: adaptive thinking + output_config.effort). But openai_compat is shared by DeepSeek, Qwen, Kimi, xAI, Gemini, etc., and each vendor accepts a different thinking parameter shape (reasoning_effort / enable_thinking / thinking:{type} / thinkingLevel). A single mapping does not work.
  2. Three levels are not enough. The industry now commonly supports 6–9 levels (minimal→max), and supported subsets vary per model (e.g. Gemini 3 Pro only low/high, GLM only high/max). Hardcoding cannot express this.
  3. Absolute level semantics add cognitive load. Users face absolute names like low/medium/high without knowing what they mean relative to the model's default. And by default kuku force-sends an effort (e.g. high→xhigh), overriding the model's native behavior on the user's behalf.
  4. No max_tokens / thinking-budget coupling. Budget-style dialects (legacy Anthropic thinking) require max_tokens >= budget + buffer, which is currently missing. (Context/max_tokens strategy is tracked separately; see the follow-up note in Proposal.)

Five reference projects were studied (codex, hermes-agent, oh-my-pi, opencode, pi — see .superpowers/reference-src/). The consensus: unified user levels + per-model/per-provider mapping + metadata-driven adaptation. None of them hardcodes three levels at the provider layer.

Goals:

  • Zero-config default: follow the model's native behavior
  • Low-cognitive-load config: relative 4-level user model
  • Advanced users can override precisely (per-model overrides + escape hatch)
  • All mapping is data-driven

Proposal

1. User-facing: relative 4-level mental model

User config Mental meaning Internal action
(unset) Don't care Do not send effort; follow the model's native default
off Turn thinking off Send disabled (clamped to lowest level for models that require thinking)
low Think less Clamp to the lowest supported level of the model
medium Standard Follow the model default (do not send)
high Think more Clamp to the highest supported level of the model

The ThinkLevel enum stays as-is (off/low/medium/high); only the semantics change from "absolute level" to "relative to default". Existing configs remain fully compatible.

2. Internal: 6-level effort enum + per-model thinking metadata

The wire layer uses the industry-standard six levels minimal/low/medium/high/xhigh/max (identical to the models.dev effort enum — zero conversion). Each model/provider resolves to three metadata fields:

struct ModelThinking {
    mode: ThinkingMode,             // dialect: effort | responses | adaptive | google-level
    supported: Vec<Effort>,         // supported levels (default: all; builtin table for mainstream models)
    default_effort: Option<Effort>, // model's native default (None = do not send)
}

3. Mapping pipeline

user config (relative 4-level) + ModelThinking → intent resolution → clamping → dialect mapping → request body
  • Intent resolution: unset/medium → do not send; low/high → endpoint semantics (lowest/highest supported level); absolute levels (minimal/xhigh/max, reachable via config) → clamp down to the nearest supported level
  • Dialect mapping: effort→reasoning_effort; responses→reasoning:{effort}; adaptive→thinking:{type:"adaptive"}+output_config:{effort}; google-level→thinkingLevel:"LOW" (xhigh/max fold to HIGH)
  • Clamping and dialects are decoupled: adding a dialect never touches clamping logic

4. Dialects: 4 built-in, the rest user-defined

Built-in: effort (OpenAI-compatible family) / responses (OpenAI Responses) / adaptive (Claude 4.6+, Kimi) / google-level (Gemini 3). Legacy Anthropic budget_tokens mode and niche dialects (qwen-toggle, etc.) are not built in — configured on demand (mode = "budget" + single value budget = 8000, auto-scaled low=budget/2, medium=budget, high=budget×2).

5. Config: optional thinking sub-table under the existing [model.<tier>]

Follows kuku's tier convention; all fields optional; zero config by default:

# ① Mainstream: no new config
[model.balanced]
provider = "anthropic"
model = "claude-sonnet-4-6"
think = "high"

# ② Custom model: 1-2 lines
[model.local]
provider = "local"
model = "qwen3-32b"
thinking.mode = "qwen-toggle"

# ③ Legacy Claude budget: single value
[model.legacy]
provider = "anthropic"
model = "claude-3-5-sonnet"
thinking.mode = "budget"
thinking.budget = 8000

Optional fields: mode (dialect), supported (levels), budget (single value), map (level→wire-value escape hatch, mirroring pi's thinkingLevelMap). All settable via kuku config set dot-notation.

6. Metadata sources

Dialect is inferred from provider format + model-name prefix (unknown models default to the "latest contract", e.g. unknown Claude → adaptive — the approach hermes-agent validates). A builtin table for mainstream models' supported levels (aligned with models.dev); unknown models default to all levels. Remote catalog refresh is deferred (possible follow-up).

7. Follow-up

max_tokens / thinking-budget coupling (max_tokens >= budget + buffer) and context-window management strategy will be tracked as a separate issue.

Alternatives considered

  • 9 levels (codex/hermes pattern): ultra is only supported by new OpenAI models; kuku is multi-provider and most vendors cannot map it. More levels = more cognitive load. Rejected.
  • Free per-model levels (opencode pattern): inconsistent user surface, dozens-of-lines model config JSON — conflicts with the low-cognitive-load goal. Rejected.
  • Large provider-level mapping tables (hermes pattern): most detailed but highest maintenance cost (per-endpoint special cases); less elegant than "dialect inference rules + sparse overrides". Rejected.
  • Builtin legacy Claude budget dialect: explicitly not needed; demoted to user config to keep the builtin surface lean.
  • auto level (oh-my-pi's classifier): requires a small-model classifier, high cost; deferred, reserved as a future extension point.

Affected scope

SDK + CLI

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions