feat: adaptive reasoning detection for Codex Chat providers

Auto-detect each Chat-routed Codex provider's reasoning interface from
its name, base URL, and model, then inject the matching thinking
parameter without manual configuration:

- Platform-first inference (OpenRouter, SiliconFlow) overrides model
  rules, since the same model exposes different reasoning controls
  depending on the hosting platform.
- Effort tiers are forwarded only to providers that support them
  (DeepSeek, OpenRouter, and StepFun's step-3.5-flash-2603); on/off-only
  providers (Kimi, GLM, Qwen, MiniMax, MiMo, SiliconFlow) drop the level
  instead of sending a field the upstream rejects.
- OpenRouter uses the native reasoning:{effort} object, clamps max to
  xhigh (its enum has no max), and forwards an explicit effort:"none" so
  reasoning can be turned off.
- StepFun falls back to inference so per-model effort support is honored
  (the static preset would have forced effort on step-3.5-flash too).

Includes the Codex provider-form reasoning controls, i18n strings
(zh/en/ja), and response-side reasoning extraction.
This commit is contained in:
Jason
2026-05-21 22:29:18 +08:00
parent 5048ed632d
commit 44d9aabbf3
13 changed files with 1058 additions and 16 deletions
@@ -16,6 +16,7 @@ import type {
ClaudeApiFormat,
CodexApiFormat,
CodexCatalogModel,
CodexChatReasoning,
ClaudeApiKeyField,
} from "@/types";
import {
@@ -174,6 +175,41 @@ export const normalizeCodexCatalogModelsForSave = (
return normalized;
};
const normalizeCodexChatReasoningForSave = (
value?: CodexChatReasoning,
): CodexChatReasoning | undefined => {
const supportsEffort = value?.supportsEffort === true;
const supportsThinking = value?.supportsThinking === true || supportsEffort;
const hasExplicitConfig = value && Object.keys(value).length > 0;
if (!supportsThinking && !supportsEffort) {
return hasExplicitConfig
? {
supportsThinking: false,
supportsEffort: false,
thinkingParam: "none",
effortParam: "none",
outputFormat: value?.outputFormat ?? "auto",
}
: undefined;
}
return {
supportsThinking,
supportsEffort,
thinkingParam: supportsThinking
? (value?.thinkingParam ?? "thinking")
: "none",
effortParam: supportsEffort
? (value?.effortParam ?? "reasoning_effort")
: "none",
effortValueMode: supportsEffort
? (value?.effortValueMode ?? "passthrough")
: undefined,
outputFormat: value?.outputFormat ?? "auto",
};
};
export interface ProviderFormProps {
appId: AppId;
providerId?: string;
@@ -316,6 +352,7 @@ function ProviderFormFull({
initialData?.meta?.pricingModelSource,
),
});
setCodexChatReasoning(initialData?.meta?.codexChatReasoning ?? {});
}, [appId, initialData, supportsFullUrl]);
const defaultValues: ProviderFormData = useMemo(
@@ -466,6 +503,10 @@ function ProviderFormFull({
const [codexFastMode, setCodexFastMode] = useState<boolean>(
() => initialData?.meta?.codexFastMode ?? false,
);
const [codexChatReasoning, setCodexChatReasoning] =
useState<CodexChatReasoning>(
() => initialData?.meta?.codexChatReasoning ?? {},
);
const {
codexAuth,
@@ -530,6 +571,7 @@ function ProviderFormFull({
if (appId === "codex" && !initialData && selectedPresetId === "custom") {
const template = getCodexCustomTemplate();
resetCodexConfig(template.auth, template.config);
setCodexChatReasoning({});
}
}, [appId, initialData, selectedPresetId, resetCodexConfig]);
@@ -1336,6 +1378,12 @@ function ProviderFormFull({
? selectedGitHubAccountId
: undefined,
codexFastMode: isCodexOauthProvider ? codexFastMode : undefined,
codexChatReasoning:
appId === "codex" &&
category !== "official" &&
localCodexApiFormat === "openai_chat"
? normalizeCodexChatReasoningForSave(codexChatReasoning)
: undefined,
testConfig: testConfig.enabled ? testConfig : undefined,
costMultiplier: pricingConfig.enabled
? pricingConfig.costMultiplier
@@ -1473,6 +1521,7 @@ function ProviderFormFull({
if (appId === "codex") {
const template = getCodexCustomTemplate();
resetCodexConfig(template.auth, template.config);
setCodexChatReasoning({});
setLocalCodexApiFormat(
codexApiFormatFromWireApi(extractCodexWireApi(template.config)) ??
"openai_responses",
@@ -1513,6 +1562,7 @@ function ProviderFormFull({
const config = preset.config ?? "";
resetCodexConfig(auth, config, preset.modelCatalog ?? []);
setCodexChatReasoning(preset.codexChatReasoning ?? {});
setLocalCodexApiFormat(
preset.apiFormat ??
codexApiFormatFromWireApi(extractCodexWireApi(config)) ??
@@ -1984,6 +2034,8 @@ function ProviderFormFull({
onAutoSelectChange={setEndpointAutoSelect}
apiFormat={localCodexApiFormat}
onApiFormatChange={handleCodexApiFormatChange}
codexChatReasoning={codexChatReasoning}
onCodexChatReasoningChange={setCodexChatReasoning}
catalogModels={codexCatalogModels}
onCatalogModelsChange={setCodexCatalogModels}
speedTestEndpoints={speedTestEndpoints}