Skip to content

thinking levels: max is never offered (shift+tab stops at high), and minimal is offered but 400s #1

Description

@km-tr

Versions — pi 0.87.1 (macOS), @commandcode/pi-commandcode-provider 0.3.0.

Problem

pi's thinking levels for every Command Code model stop at high. max never shows up when cycling with shift+tab or in /thinking, and asking for it on the command line is silently clamped back to high:

$ pi -p --model command-code/deepseek/deepseek-v4.1-flash:max "reply with exactly: ok"
ok
$ # the session log records the level that was actually used:
$ grep thinking_level_change ~/.pi/agent/sessions/**/*.jsonl
{"type":"thinking_level_change","thinkingLevel":"high"}

PI_REASONING_LEVEL=max behaves the same way, and nothing warns that the request was downgraded. This applies to all 81 models in the catalog, not just DeepSeek, and no configuration reaches max — modelThinkingLevels and defaultThinkingLevel are clamped the same way.

max is a value the gateway accepts (POST /provider/v1/chat/completions, deepseek/deepseek-v4.1-flash, max_tokens: 16):

reasoning_effort gateway
low / medium / high / max 200
minimal 400 (see below)

Why

getSupportedThinkingLevels() in @earendil-works/pi-ai:

const EXTENDED_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
export function getSupportedThinkingLevels(model) {
    if (!model.reasoning) return ["off"];
    return EXTENDED_THINKING_LEVELS.filter((level) => {
        const mapped = model.thinkingLevelMap?.[level];
        if (mapped === null) return false;
        if (level === "xhigh" || level === "max") return mapped !== undefined;  // opt-in
        return true;
    });
}

xhigh and max are opt-in — pi only offers them when thinkingLevelMap names them, and docs/rpc-commands.md:278 describes them as levels "exposed only when supported by the selected model". toModel never sets thinkingLevelMap, so the offered set is off | minimal | low | medium | high, and clampThinkingLevel() pulls anything above high back down to it.

The mirror image: minimal is offered but rejected

The same missing map makes the fallback "every level that is not opt-in is supported", so pi offers minimal and the gateway answers 400:

$ pi -p --model command-code/deepseek/deepseek-v4.1-flash:minimal "reply with exactly: ok"
400: {"message":"Invalid option: expected one of \"low\"|\"medium\"|\"high\"|\"xhigh\"|\"max\"","type":"invalid_request_error","param":"reasoning_effort"}

Reproduced against 12 models on the /chat/completions route — deepseek/deepseek-v4.1-flash, deepseek/deepseek-v4-pro, Qwen/Qwen3.8-Max, MiniMaxAI/MiniMax-M3, moonshotai/Kimi-K3, zai-org/GLM-5.3, google/gemini-3.8-flash, xai/grok-4.7, meta/muse-spark-1.3, xiaomi/mimo-v2.6-pro, sakana/fugu-ultra, gpt-6-astra — all return that same message. off is unaffected: pi omits the field entirely and the request returns 200. The /messages route (Claude) is untested here, plan-limited on this key.

Fix

Only minimal is a clear-cut bug — the gateway rejects it, so it should not be offered:

model.thinkingLevelMap = { minimal: null };

Adding max: "max" on top of that puts max back into shift+tab for the /chat/completions models, which is what this report is about:

model.thinkingLevelMap = { minimal: null, max: "max" };

Whether max deserves a place on every open model, and whether xhigh belongs anywhere here, is a call for the maintainer — xhigh is an OpenAI-style value that pi treats as a per-model opt-in, and nothing in the catalog currently claims it. /provider/v1/models returns no capability fields today (id, object, created, owned_by, name, context_length, supported_endpoints), so a per-model reasoning_efforts list there would let toModel derive the whole map instead of hardcoding one, the same way pricing, max_output_tokens, modalities and reasoning are already read when the API sends them.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions