Skip to content

Effort & Context

How reasoning effort, token counting, and the context indicator behave for mapped models.

Updated View as Markdown

Reasoning effort

Claude Code’s effort level (/effort, the /model slider, --effort, or CLAUDE_CODE_EFFORT_LEVEL) is sent as the output_config.effort request field, and shunt maps it to the Responses reasoning.effort for mapped models:

Claude Code effort reasoning.effort
low / medium / high / xhigh passthrough
max passthrough on models that accept it (the gpt-5.6 and gpt-6 families), else folded to xhigh

Which reasoning levels a Codex slug accepts is listed per-model in openai/codex’s models.json (supported_reasoning_levels).

Precedence in shunt: a config route.effort / [providers.*].effort override wins first; otherwise the request’s output_config.effort is honored; otherwise thinking.enabled → high, then a model-name suffix (-xhigh/-high/-medium/-low, with -spark treated as -low), else medium.

Token counting (count_tokens)

For an Anthropic-routed model shunt passes POST /v1/messages/count_tokens through to the upstream (exact counts). For a responses-routed model there is no equivalent upstream endpoint, so the provider’s count_tokens setting decides:

  • count_tokens = "tiktoken" (default) — shunt computes the count locally with tiktoken’s o200k_base encoder and returns {"input_tokens": N}. Near-exact for text on GPT-family models, and answered in-process (~ms) — which matters because Claude Code’s /context issues one count_tokens call per displayed item (30–50 calls per invocation).
  • count_tokens = "estimate" (opt-in) — shunt returns 501 not_supported, telling Claude Code that the endpoint is unavailable and triggering its fallback. The main-loop context bar then estimates locally, but /context re-runs every category count against Haiku over the network — slow, and silently reported as 0 tokens when no Anthropic credential is available.

Either way the request never reaches the responses adapter, so a count request is never turned into (and billed as) a full inference call.

Context / usage display for mapped models

Claude Code computes the context indicator locally from the assistant message’s token usage divided by the model’s context-window size. For models routed to a responses provider:

  • Token count (the numerator) is accurate. shunt reads input_tokens and the input_tokens_details from the Responses usage and forwards them in the Anthropic message_delta, peeling cached_tokens into cache_read_input_tokens and cache_write_tokens into cache_creation_input_tokens so the three fields still sum to the prompt total (an upstream that doesn’t report cache_write_tokens leaves cache_creation_input_tokens at 0). This is what the main-loop context bar and /context chart against.
  • The window (the denominator) defaults to a fixed 200k for unrecognized ids. A model with a larger real window (e.g. gpt-5.6-sol at 372k) shows a conservative, over-reported percentage — this only makes auto-compact trigger a little early.

The agent panel’s per-subagent token indicator (and the subagentStatusLine tokenCount) is a separate consumer: it reads the usage from the opening message_start snapshot, not the merged completion usage the main bar uses. The Responses API only reports usage at response.completed, so shunt seeds message_start’s usage.input_tokens with the same local tiktoken estimate used for count_tokens (gated on count_tokens = "tiktoken", the default). Without that seed a codex subagent would sit at a stuck 0 in the agent panel even while its main context bar was correct, because Claude Code reads that first snapshot once and never re-reads the merged total. The accurate figure still arrives in message_delta; the message_start value is a text-only estimate (same caveats as count_tokens) that a provider set to count_tokens = "estimate" opts out of (leaving message_start at 0).

The 200k default can be overridden client-side with CLAUDE_CODE_MAX_CONTEXT_TOKENS (Claude Code 2.1.205+); it applies to any model id that does not start with claude-:

# e.g. gpt-5.6-sol's real window
export CLAUDE_CODE_MAX_CONTEXT_TOKENS=372000

Because the override applies only to ids that don’t start with claude-, a discovery alias (which must begin with claude-) can’t take it — its window stays pinned at the 200k default. Convenient in the picker, but use a non-claude- id (via ANTHROPIC_CUSTOM_MODEL_OPTION, or by remapping the tier aliases) when you need the accurate window. When two mapped tiers share a window — gpt-5.6-sol and gpt-5.6-luna are both 372k — one global value covers both.

The other client-side lever is the [1m] model-id suffix, which forces a 1M window — only use it when the upstream really has that window. (shunt strips a trailing [1m] before route matching and forwarding, so the hint stays purely client-side and the provider never sees it.)

For Claude models the suffix is not optional behind shunt: Claude Code only treats a natively-1M model as 1M when the base URL is api.anthropic.com, so opus and fable fall back to 200K here unless you write opus[1m] / fable[1m]. See Model Aliases & 1M Context.

Field Mapped (responses) model Claude passthrough
Context tokens used ✅ accurate (forwarded by shunt) ✅ accurate
Context window (denominator) ⚠️ 200k default; set CLAUDE_CODE_MAX_CONTEXT_TOKENS ✅ exact
count_tokens (pre-flight) ⚠️ local tiktoken count (default) ✅ exact (upstream)
rate_limits (5h / weekly) ❌ needs Anthropic headers ✅ shown

Context overflow recovery

When a conversation outgrows the upstream model’s real window, the provider rejects the request with its own wording — OpenAI’s context_length_exceeded, "This model's maximum context length is N tokens…", or a proxy’s "prompt token count of N exceeds the limit of M". Claude Code’s automatic compact-and-retry only fires on Anthropic’s phrasing, so unrewritten these errors would strand the session until a manual /compact (documented gateway pitfall).

shunt detects context-overflow errors on responses-routed models and rewrites them into the Anthropic shape Claude Code matches:

{"type": "error", "error": {"type": "invalid_request_error", "message": "prompt is too long: 372982 tokens > 272000 maximum"}}

When the upstream message carries both token counts, shunt preserves them (whatever order the upstream states them in) — Claude Code parses the N tokens > M maximum gap and compacts past the whole overshoot in a single retry. When the upstream gives no counts (e.g. the Responses API’s plain “Your input exceeds the context window of this model”), shunt emits prompt is too long alone, which still triggers compaction. Non-overflow errors are passed through with their original message.

Attribution block

Claude Code prepends an attribution line to the system prompt. Anthropic strips it before processing, but shunt forwards it unchanged, so a mapped provider receives it as the first line of instructions. It’s harmless but meaningless noise for a non-Anthropic model. To drop it:

export CLAUDE_CODE_ATTRIBUTION_HEADER=0

This is global, so it also removes attribution from Anthropic-passthrough traffic (used for cost tracking) — fine when you’re routing to another provider.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close