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
+23
View File
@@ -261,6 +261,26 @@ pub struct ClaudeDesktopModelRoute {
pub supports_1m: Option<bool>,
}
/// Codex Responses -> Chat Completions 的 reasoning 能力描述。
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct CodexChatReasoningConfig {
#[serde(rename = "supportsThinking", skip_serializing_if = "Option::is_none")]
pub supports_thinking: Option<bool>,
#[serde(rename = "supportsEffort", skip_serializing_if = "Option::is_none")]
pub supports_effort: Option<bool>,
#[serde(rename = "thinkingParam", skip_serializing_if = "Option::is_none")]
pub thinking_param: Option<String>,
#[serde(rename = "effortParam", skip_serializing_if = "Option::is_none")]
pub effort_param: Option<String>,
#[serde(rename = "effortValueMode", skip_serializing_if = "Option::is_none")]
pub effort_value_mode: Option<String>,
/// 声明性字段:标注上游 reasoning 的回传位置(reasoning_content / reasoning /
/// reasoning_details / think_tags)。当前响应侧 `extract_reasoning_field_text`
/// 靠穷举字段提取、并不读取本字段;保留作文档说明与未来按格式分发(如 think_tags)的预留。
#[serde(rename = "outputFormat", skip_serializing_if = "Option::is_none")]
pub output_format: Option<String>,
}
/// 供应商元数据
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ProviderMeta {
@@ -339,6 +359,9 @@ pub struct ProviderMeta {
/// Codex OAuth FAST mode: inject `service_tier = "priority"` for ChatGPT Codex requests.
#[serde(rename = "codexFastMode", skip_serializing_if = "Option::is_none")]
pub codex_fast_mode: Option<bool>,
/// Codex Responses -> Chat Completions reasoning capability metadata.
#[serde(rename = "codexChatReasoning", skip_serializing_if = "Option::is_none")]
pub codex_chat_reasoning: Option<CodexChatReasoningConfig>,
/// 累加模式应用中,该 provider 是否已写入 live config。
/// `None` 表示旧数据/未知状态,`Some(false)` 表示明确仅存在于数据库中。
#[serde(rename = "liveConfigManaged", skip_serializing_if = "Option::is_none")]