Skip to content

Forward the request's reasoning_effort to OpenAI-compatible providers - #185

Draft
adambalogh wants to merge 2 commits into
mainfrom
claude/deepseek-thinking-level-kgi2e5
Draft

adambalogh wants to merge 2 commits into
mainfrom
claude/deepseek-thinking-level-kgi2e5

Conversation

@adambalogh

@adambalogh adambalogh commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Why

The gateway accepted a reasoning_effort field on chat requests but never sent it on to the provider, so every model ran at its provider's default. DeepSeek V4 thinks at effort high by default and opened every reply with a long reasoning pass, and no client could turn that down.

An earlier revision of this PR turned DeepSeek's thinking off with a per-model registry flag. This revision drops that flag: the client now chooses per request with the standard OpenAI field, and the gateway translates it once per provider, with no lists of models.

What

  • Request: reasoning_effort is parsed, checked against REASONING_EFFORTS (none, minimal, low, medium, high, xhigh, max; anything else is a 400) and passed to get_chat_model_cached, where it is part of the cache key.
  • Translation, once per provider:
    • OpenAI, xAI, Z.ai, abliteration.ai, ModelArk: sent as-is. On OpenAI's Responses API path, langchain moves it into reasoning.effort.
    • ModelArk "none": sends thinking: {"type": "disabled"} instead, since ModelArk rejects reasoning_effort alongside that switch.
    • OpenRouter: reasoning: {"effort": ...}. OpenRouter lists DeepSeek V4 Pro 0813 as accepting none/low/high/max, with reasoning not mandatory.
    • Anthropic, Google: not translated yet, so the field is ignored there.
  • Omitted sends nothing, so every request that doesn't set the field reaches its provider exactly as before.
  • A value the gateway knows but the model doesn't support (for example low on a model that only offers high/max) comes back as the provider's 4xx, passed through unchanged.
  • Signed request hash: the field is included only when sent, like aspect_ratio and resolution.
  • OpenAPI: the old enum (low/medium/high, default medium) would have rejected none at connexion's body validation, which the OHTTP inner request also goes through. The enum now lists every accepted value and has no default. The request model's own "medium" default is removed too.

Compatibility

A client that already sent reasoning_effort (it was silently dropped until now) will see it reach the provider, and will find it in the signed request hash. A verifier that rebuilds the canonical request on its own has to add the field when it sends it. chat-app does that in its companion PR.

Testing

  • pytest tee_gateway/test/ tests/test_pricing.py tests/test_opengradient_field.py: 512 passed
  • New tests:
    • each provider's translation (ModelArk none vs a level, OpenRouter, OpenAI) and omitted → nothing sent;
    • an unknown value is a 400;
    • the value reaches the model factory;
    • the hash covers the field only when sent;
    • "none" passes schema validation. Run against the old openapi.yaml, this test fails.
  • ruff check, ruff format --check, mypy: clean
  • Not tried against live providers (no keys here). After deploy, a DeepSeek request with reasoning_effort: "none" should come back with reasoning_tokens: 0.

Needs a new enclave image and PCR measurements and a redeploy. Deploy this before any client starts sending the field: an old gateway leaves it out of the signed hash, so a client that includes it would fail verification.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SQvDLwdbHbxxCg4dGzeyh2

Neither request carried a reasoning setting, so both ran at the
provider default: thinking on at effort "high". For V4 that is the
lowest effort there is (ModelArk only offers high/max), so a long
reasoning pass opened every reply. Off is the only way to tone it down.

A new registry flag, disable_thinking, becomes each provider's own off
switch: ModelArk `thinking: {"type": "disabled"}` (Flash) and OpenRouter
`reasoning: {"effort": "none"}` (Pro; reasoning is not mandatory on the
0813 slug and Phala lists `reasoning` as supported). A test pins the
flag to those two providers, since the others would silently ignore it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQvDLwdbHbxxCg4dGzeyh2
Replaces the disable_thinking registry flag: the request's own
reasoning_effort field (OpenAI's standard name) is now validated,
forwarded and signed, and the client decides per request. It is
translated once per provider, not per model:

- OpenAI, xAI, Z.ai, abliteration.ai, ModelArk: sent as-is (langchain
  moves it into `reasoning` on the Responses API path).
- ModelArk "none": `thinking: {"type": "disabled"}` alone, since ModelArk
  rejects reasoning_effort alongside it.
- OpenRouter: its `reasoning: {"effort": ...}` object.
- Anthropic, Google: not translated yet; the field is ignored.

Omitted sends nothing, so every existing request builds exactly as
before. An unknown value is a 400; a known value a model doesn't
support is the provider's 4xx, passed through. The field joins the
signed request hash only when sent, like aspect_ratio and resolution.

The OpenAPI enum (low/medium/high, default medium) would have refused
"none" at connexion's body validation, which the OHTTP inner request
also goes through; it now lists every accepted value and has no
default. The request model's own "medium" default is gone too, so an
unset field can never reach a provider or the hash.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQvDLwdbHbxxCg4dGzeyh2
@adambalogh adambalogh changed the title Turn thinking off for DeepSeek V4 Flash and V4 Pro Forward the request's reasoning_effort to OpenAI-compatible providers Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants