fix(aiCore): bypass AI SDK model ID allowlist for reasoning detection (#13463)

### What this PR does

Before this PR:

When using reasoning models (e.g., GPT-5.2) through third-party
OpenAI-compatible providers that use the Responses API endpoint,
`reasoningEffort` and `reasoningSummary` settings from the UI are
silently ignored. The AI SDK logs a warning: `reasoningEffort is not
supported for non-reasoning models`.

After this PR:

Reasoning parameters are correctly included in the Responses API request
body for all providers, regardless of model ID format.

Fixes #13454

### Why we need it and why it was done in this way

The following tradeoffs were made:

The `@ai-sdk/openai` Responses model uses a hardcoded model ID allowlist
(`modelId.startsWith('gpt-5')`, etc.) to determine if a model supports
reasoning. Third-party providers often use non-canonical model IDs
(e.g., `openai/gpt-5.2`) that fail these checks, causing reasoning
params to be silently dropped.

The fix uses `forceReasoning: true` — a bypass mechanism provided by the
AI SDK — when Cherry Studio's own `isReasoningModel()` detects the model
as a reasoning model. This is semantically correct: Cherry Studio's
model detection is more comprehensive than the AI SDK's allowlist.

The following alternatives were considered:

- Migrating to `@ai-sdk/open-responses` (tracked in #13462) — better
long-term solution but larger scope
- Stripping model ID prefixes before passing to the AI SDK — fragile and
provider-specific
- Using `openai-compatible` provider with manual reasoning parameter
handling — would lose Responses API features

### Breaking changes

None.

### Testing

- [x] Manual test passed: confirmed `reasoning.effort` and
`reasoning.summary` are now correctly included in the Responses API
request body when using a third-party provider with non-canonical model
IDs.

### Special notes for your reviewer

- The `forceReasoning` option is an official AI SDK feature, not a hack.
It's documented for exactly this use case: "stealth" reasoning models
where the model ID is not recognized by the SDK's allowlist.
- `isReasoningModel(model)` is used instead of `enableReasoning` because
`forceReasoning` should reflect model capability, not user preference.
- A TODO comment links to #13462 for the longer-term migration to
`@ai-sdk/open-responses`.

### Checklist

- [x] PR: The PR description is expressive enough and will help future
contributors
- [x] Code: Write code that humans can understand and Keep it simple
- [x] Refactor: You have left the code cleaner than you found it (Boy
Scout Rule)
- [ ] Upgrade: Impact of this change on upgrade flows was considered and
addressed if required
- [ ] Documentation: A user-guide update was considered and is present
(link) or not required.
- [x] Self-review: I have reviewed my own code before requesting review
from others

### Release note

```release-note
fix: reasoning effort and summary mode settings now correctly apply for third-party OpenAI-compatible providers using the Responses API endpoint
```

Signed-off-by: icarus <eurfelux@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Phantom
2026-03-14 19:25:14 +08:00
committed by GitHub
parent d676b3b39a
commit f3244c3e9e

View File

@@ -13,6 +13,7 @@ import {
isOpenAIModel,
isOpenAIOpenWeightModel,
isQwenMTModel,
isReasoningModel,
isSupportFlexServiceTierModel,
isSupportVerbosityModel
} from '@renderer/config/models'
@@ -382,7 +383,12 @@ function buildOpenAIProviderOptions(
const reasoningParams = getOpenAIReasoningParams(assistant, model)
providerOptions = {
...providerOptions,
...reasoningParams
...reasoningParams,
// TODO: Remove this workaround after migrating to @ai-sdk/open-responses (#13462)
// Bypass @ai-sdk/openai's model ID allowlist for reasoning detection.
// Third-party providers often use non-canonical model IDs (e.g., "openai/gpt-5.2")
// that fail the SDK's startsWith() checks, causing reasoning params to be silently dropped.
...(isReasoningModel(model) && { forceReasoning: true })
}
}
const provider = getProviderById(model.provider)