Skip to content

Optional reasoning-effort control for remote (OpenAI-compatible) models #17

Description

@fl4p

Summary

Add an optional reasoningEffort control for reasoning-capable remote models (Qwen3, DeepSeek-R1, OpenAI o-series/gpt-5, etc.) served through the OpenAI-compatible provider. Today GenerationOptions only exposes enableThinking (a boolean) plus temperature/topP — there is no way to ask a reasoning model to think harder or lighter per chat.

Deferred out of the DSML/tool-loop fix branch (feat/serper-search-provider) to keep that change focused.

Why it's not trivial: you can't discover which levels a model supports

Verified empirically against opencode.ai/zen: its /v1/models returns only the bare OpenAI fields per model —

{"id":"big-pickle","object":"model","created":1783094914,"owned_by":"opencode"}

No capabilities, no supported_parameters, nothing about reasoning effort. There is no standard OpenAI mechanism to enumerate supported reasoning-effort values:

  • reasoning_effort (minimal|low|medium|high on OpenAI o-series/gpt-5) is a request parameter, not a discoverable capability.
  • The one exception in the wild is aggregators like OpenRouter, which put a supported_parameters array on each model object (can include reasoning/reasoning_effort). Most OpenAI-compatible servers (including zen) do not.

Risk: blindly sending it can 400

Strict OpenAI-compatible servers reject unknown fields. The app already gates chat_template_kwargs to LM Studio only for exactly this reason (openAICompatibleProvider.buildRequestBody). So reasoning_effort must be opt-in and gated, not sent to every server.

Proposed design

  1. Add reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high' to GenerationOptions (src/services/providers/types.ts).
  2. In model discovery, opportunistically read supported_parameters when a server provides it (OpenRouter-style) and store it on the model capabilities. Absent that, treat support as unknown.
  3. In buildRequestBody (src/services/providers/openAICompatibleProvider.ts), include reasoning_effort only when the user has set it and (the server advertised support or the user explicitly forced it).
  4. UI: a per-chat / per-server control, only shown for models where it's plausibly supported.
  5. Leave the tool-loop forced-final / synthesis passes with thinking disabled regardless — a reasoning model given room to think there can re-decide to keep searching, which is the loop we're trying to terminate.

Notes

  • big-pickle itself reports supportsThinking: false, so it would not use this — the feature targets genuinely reasoning-capable remotes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions