diff --git a/changelog.d/features/command-code-reasoning-efforts.md b/changelog.d/features/command-code-reasoning-efforts.md new file mode 100644 index 0000000000..3e9b172204 --- /dev/null +++ b/changelog.d/features/command-code-reasoning-efforts.md @@ -0,0 +1 @@ +- feat(command-code): advertise low/medium/high/xhigh/max reasoning-effort suffixes for reasoning-capable models in the catalog and Combo Builder, with request-time resolution to reasoning_effort diff --git a/open-sse/config/providers/registry/command-code/index.ts b/open-sse/config/providers/registry/command-code/index.ts index 23a73efe46..77b08ab8ce 100644 --- a/open-sse/config/providers/registry/command-code/index.ts +++ b/open-sse/config/providers/registry/command-code/index.ts @@ -1,5 +1,7 @@ import type { RegistryEntry } from "../../shared.ts"; +const COMMAND_CODE_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"] as const; + export const command_codeProvider: RegistryEntry = { id: "command-code", alias: "cmd", @@ -20,6 +22,7 @@ export const command_codeProvider: RegistryEntry = { id: "claude-opus-4-7", name: "Claude Opus 4.7 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 200000, maxOutputTokens: 32000, @@ -28,6 +31,7 @@ export const command_codeProvider: RegistryEntry = { id: "claude-opus-4-6", name: "Claude Opus 4.6 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 200000, maxOutputTokens: 32000, @@ -36,6 +40,7 @@ export const command_codeProvider: RegistryEntry = { id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 200000, maxOutputTokens: 16384, @@ -44,6 +49,7 @@ export const command_codeProvider: RegistryEntry = { id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 200000, maxOutputTokens: 8192, @@ -52,6 +58,7 @@ export const command_codeProvider: RegistryEntry = { id: "gpt-5.5", name: "GPT-5.5 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 256000, maxOutputTokens: 128000, @@ -60,6 +67,7 @@ export const command_codeProvider: RegistryEntry = { id: "gpt-5.4", name: "GPT-5.4 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 256000, maxOutputTokens: 128000, @@ -68,6 +76,7 @@ export const command_codeProvider: RegistryEntry = { id: "gpt-5.3-codex", name: "GPT-5.3 Codex (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 256000, maxOutputTokens: 128000, @@ -75,7 +84,8 @@ export const command_codeProvider: RegistryEntry = { { id: "gpt-5.4-mini", name: "GPT-5.4 Mini (CC)", - supportsReasoning: false, + supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 256000, maxOutputTokens: 128000, @@ -84,6 +94,7 @@ export const command_codeProvider: RegistryEntry = { id: "deepseek/deepseek-v4-pro", name: "DeepSeek V4 Pro (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 1000000, maxOutputTokens: 131072, }, @@ -91,6 +102,7 @@ export const command_codeProvider: RegistryEntry = { id: "deepseek/deepseek-v4-flash", name: "DeepSeek V4 Flash (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 1000000, maxOutputTokens: 131072, }, @@ -98,6 +110,7 @@ export const command_codeProvider: RegistryEntry = { id: "moonshotai/Kimi-K2.6", name: "Kimi K2.6 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 262144, maxOutputTokens: 65536, @@ -106,6 +119,7 @@ export const command_codeProvider: RegistryEntry = { id: "moonshotai/Kimi-K2.5", name: "Kimi K2.5 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 262144, maxOutputTokens: 65536, @@ -114,6 +128,7 @@ export const command_codeProvider: RegistryEntry = { id: "zai-org/GLM-5.1", name: "GLM-5.1 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 200000, maxOutputTokens: 32768, }, @@ -121,6 +136,7 @@ export const command_codeProvider: RegistryEntry = { id: "zai-org/GLM-5", name: "GLM-5 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 200000, maxOutputTokens: 32768, }, @@ -128,6 +144,7 @@ export const command_codeProvider: RegistryEntry = { id: "MiniMaxAI/MiniMax-M2.7", name: "MiniMax M2.7 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 1048576, maxOutputTokens: 65536, }, @@ -135,6 +152,7 @@ export const command_codeProvider: RegistryEntry = { id: "MiniMaxAI/MiniMax-M2.5", name: "MiniMax M2.5 (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 1048576, maxOutputTokens: 65536, }, @@ -142,6 +160,7 @@ export const command_codeProvider: RegistryEntry = { id: "Qwen/Qwen3.6-Max-Preview", name: "Qwen 3.6 Max (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, contextLength: 1000000, maxOutputTokens: 32768, }, @@ -149,6 +168,7 @@ export const command_codeProvider: RegistryEntry = { id: "Qwen/Qwen3.6-Plus", name: "Qwen 3.6 Plus (CC)", supportsReasoning: true, + supportedThinkingEfforts: COMMAND_CODE_REASONING_EFFORTS, supportsVision: true, contextLength: 1000000, maxOutputTokens: 32768, diff --git a/src/lib/providerModels/modelDiscovery.ts b/src/lib/providerModels/modelDiscovery.ts index 5b41227f24..923e3b8509 100644 --- a/src/lib/providerModels/modelDiscovery.ts +++ b/src/lib/providerModels/modelDiscovery.ts @@ -98,6 +98,11 @@ const EFFORT_SYNONYMS: Record = { max: "xhigh" }; // Live request testing confirms Crof accepts `max` as a distinct top tier. const CROF_REASONING_EFFORTS = ["none", "low", "medium", "high", "max"] as const; +// Command Code's provider API accepts the documented low/medium/high/xhigh/max +// reasoning_effort values for its reasoning-capable model catalog, but its +// /models response does not declare them. Keep this fallback provider-scoped. +const COMMAND_CODE_REASONING_EFFORTS = ["low", "medium", "high", "xhigh", "max"] as const; + function normalizeSupportedEffort(effort: string): string { if ((CANONICAL_EFFORT_VALUES as readonly string[]).includes(effort)) return effort; return EFFORT_SYNONYMS[effort.toLowerCase()] || effort; @@ -232,6 +237,7 @@ export function normalizeDiscoveredModels( if (!id) continue; const isCrofReasoningModel = providerId === "crof" && record.reasoning_effort === true; + const isCommandCodeModel = providerId === "command-code"; const supportedThinkingEfforts = (() => { // The flat import field and every recognized upstream tier array remain // authoritative over the provider fallback, including an explicit empty list. @@ -242,7 +248,8 @@ export function normalizeDiscoveredModels( } const detected = detectSupportedThinkingEfforts(record); if (detected || hasDeclaredEffortList(record)) return detected; - return isCrofReasoningModel ? [...CROF_REASONING_EFFORTS] : undefined; + if (isCrofReasoningModel) return [...CROF_REASONING_EFFORTS]; + return isCommandCodeModel ? [...COMMAND_CODE_REASONING_EFFORTS] : undefined; })(); const name = @@ -308,7 +315,7 @@ export function normalizeDiscoveredModels( ...(typeof record.description === "string" ? { description: record.description } : {}), ...(typeof record.supportsThinking === "boolean" ? { supportsThinking: record.supportsThinking } - : isCrofReasoningModel + : isCrofReasoningModel || isCommandCodeModel ? { supportsThinking: true } : {}), ...(record.alwaysThinking === true ? { alwaysThinking: true } : {}), diff --git a/tests/unit/combo-builder-effort-variants-8072.test.ts b/tests/unit/combo-builder-effort-variants-8072.test.ts index 86db44fdc0..5a1ffe7eec 100644 --- a/tests/unit/combo-builder-effort-variants-8072.test.ts +++ b/tests/unit/combo-builder-effort-variants-8072.test.ts @@ -199,3 +199,52 @@ test("#9485 Crof synced effort aliases appear exactly in the Combo Builder picke ); assert.deepEqual(actualAliases, expectedAliases); }); + +test("Command Code static reasoning models expose all documented effort suffixes", async () => { + const connection = await providersDb.createProviderConnection({ + provider: "command-code", + authType: "apikey", + name: "command-code-efforts", + apiKey: "command-code-key", + isActive: true, + testStatus: "active", + }); + assert.ok(connection); + + const payload = await getComboBuilderOptions(); + const provider = payload.providers.find((entry) => entry.providerId === "command-code"); + assert.ok(provider, "command-code provider must appear in the combo builder output"); + + const reasoningModels = [ + "claude-opus-4-7", + "claude-opus-4-6", + "claude-sonnet-4-6", + "claude-haiku-4-5-20251001", + "gpt-5.5", + "gpt-5.4", + "gpt-5.4-mini", + "gpt-5.3-codex", + "deepseek/deepseek-v4-pro", + "deepseek/deepseek-v4-flash", + "moonshotai/Kimi-K2.6", + "moonshotai/Kimi-K2.5", + "zai-org/GLM-5.1", + "zai-org/GLM-5", + "MiniMaxAI/MiniMax-M2.7", + "MiniMaxAI/MiniMax-M2.5", + "Qwen/Qwen3.6-Max-Preview", + "Qwen/Qwen3.6-Plus", + ]; + const effortTiers = ["low", "medium", "high", "xhigh", "max"]; + for (const baseId of reasoningModels) { + const base = provider!.models.find((model) => model.id === baseId); + assert.ok(base, `${baseId} base model must appear in the model picker`); + for (const effort of effortTiers) { + const alias = provider!.models.find((model) => model.id === `${baseId}-${effort}`); + assert.ok(alias, `${baseId}-${effort} must appear in the model picker`); + assert.equal(alias!.contextLength, base!.contextLength); + assert.equal(alias!.outputTokenLimit, base!.outputTokenLimit); + assert.equal(alias!.supportsThinking, true); + } + } +}); diff --git a/tests/unit/model-discovery-reasoning-levels.test.ts b/tests/unit/model-discovery-reasoning-levels.test.ts index 01846d3882..fe04200718 100644 --- a/tests/unit/model-discovery-reasoning-levels.test.ts +++ b/tests/unit/model-discovery-reasoning-levels.test.ts @@ -113,6 +113,15 @@ test("CrofAI reasoning_effort true maps to the supported effort tiers", () => { assert.deepEqual(model.supportedThinkingEfforts, ["none", "low", "medium", "high", "max"]); }); +test("Command Code discovery supplies provider-wide effort tiers when /models omits them", () => { + const [model] = normalizeDiscoveredModels( + [{ id: "command-code-live-model", name: "Command Code Live Model" }], + "command-code" + ); + assert.equal(model.supportsThinking, true); + assert.deepEqual(model.supportedThinkingEfforts, ["low", "medium", "high", "xhigh", "max"]); +}); + test("CrofAI false or absent reasoning_effort adds no reasoning metadata", () => { const models = normalizeDiscoveredModels( [{ id: "crof-no-reasoning", reasoning_effort: false }, { id: "crof-unknown-reasoning" }],