Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/fixes/command-code-effort-capabilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- fix(combo): resolve effort-suffixed command-code variants (e.g. `deepseek-v4-flash-max`) to their base model for capability lookups, so tool-bearing combo requests keep the declared priority order instead of reordering behind models with confirmed capabilities
62 changes: 59 additions & 3 deletions src/lib/modelCapabilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,39 @@ function leafModelId(modelId: string | null | undefined): string | null {
return leaf && leaf !== modelId ? leaf : null;
}

/**
* Effort suffixes the catalog synthesizes as `<base>-<tier>` variant ids from a
* base model's `supportedThinkingEfforts` (mirrors REGISTERED_EFFORT_SUFFIXES
* in open-sse/utils/registeredEffortVariants.ts, plus `minimal` for muse).
*/
const EFFORT_VARIANT_SUFFIXES = [
"none",
"minimal",
"low",
"medium",
"high",
"xhigh",
"max",
] as const;

/**
* Strip a trailing effort-tier suffix off a model id (e.g.
* `deepseek-v4-flash-max` → `deepseek-v4-flash`). Longest token first so
* `xhigh` is matched before `high`. Returns null when no known suffix matches
* or the id would be left empty.
*/
function stripKnownEffortSuffix(modelId: string): string | null {
const normalized = String(modelId || "").trim();
if (!normalized) return null;
for (const suffix of EFFORT_VARIANT_SUFFIXES) {
const token = `-${suffix}`;
if (normalized.length > token.length && normalized.endsWith(token)) {
return normalized.slice(0, -token.length);
}
}
return null;
}

function getStaticSpec(modelId: string | null, rawModel: string | null): ModelSpec | undefined {
if (modelId) {
const byCanonical = getModelSpec(modelId);
Expand Down Expand Up @@ -704,15 +737,38 @@ export function getResolvedModelCapabilities(
// persisted override never feeds back into the comparison that (re)writes it.
const usePersistedOverrides = options?.persistedOverrides !== false;
const resolved = resolveCapabilityInput(input);
const spec = getStaticSpec(resolved.model, resolved.rawModel);
const registryModel = getRegistryModel(resolved.provider, resolved.model);
const synced = getSyncedCapabilityForResolved(
let spec = getStaticSpec(resolved.model, resolved.rawModel);
let registryModel = getRegistryModel(resolved.provider, resolved.model);
let synced = getSyncedCapabilityForResolved(
resolved.provider,
resolved.model,
resolved.rawModel,
snapshot
);

// Effort-suffixed variants (e.g. command-code `deepseek-v4-flash-max`,
// `meta/muse-spark-1.2-contributor-xhigh`) are synthesized in the catalog
// from the base model's `supportedThinkingEfforts`; they have no registry
// row, synced row, or static spec of their own. Without a base-model
// fallback the variant resolves with NULL tool/vision/context capabilities,
// so a tool-bearing combo request treats the target as incompatible and
// silently reorders it behind models with confirmed capabilities. Resolve
// the variant's capabilities from its base model when every direct source
// misses.
if (!spec && !registryModel && !synced && resolved.provider && resolved.model) {
const baseModelId = stripKnownEffortSuffix(resolved.model);
if (baseModelId && baseModelId !== resolved.model) {
spec = getStaticSpec(baseModelId, resolved.rawModel);
registryModel = getRegistryModel(resolved.provider, baseModelId);
synced = getSyncedCapabilityForResolved(
resolved.provider,
baseModelId,
resolved.rawModel,
snapshot
);
}
}

const modalitiesInput = parseModalities(synced?.modalities_input);
const modalitiesOutput = parseModalities(synced?.modalities_output);
const lookupKey =
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/command-code-registry-vision.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,53 @@ test("MiniMax M3 via command-code keeps existing vision capability (no regressio
const caps = getResolvedModelCapabilities("command-code/MiniMaxAI/MiniMax-M3");
assert.equal(caps.supportsVision, true);
});

test("command-code effort-suffixed variants resolve capabilities from their base model", async () => {
// Effort variants (e.g. `-max`, `-xhigh`) are synthesized from the base's
// supportedThinkingEfforts and have no registry/synced row of their own.
// Without the base-model fallback they resolve NULL tool/vision/context,
// which makes a tool-bearing combo request drop them behind confirmed
// targets (observed: orchestrator tried opencode-go/mimo-v2.5-max at
// position 2 while command-code deepseek sat unused at its declared
// priority position 2).
//
// Seed the models.dev capability store (the source getResolvedModelCapabilities
// reads for tool/vision/context) with the base models, then verify the
// effort-suffixed variants inherit those capabilities via the base fallback.
const modelsDevSync = await import("../../src/lib/modelsDevSync.ts");
modelsDevSync.saveModelsDevCapabilities({
"command-code": {
"deepseek/deepseek-v4-flash": {
tool_call: true,
reasoning: true,
attachment: false,
limit_context: 1000000,
limit_input: 1000000,
limit_output: 131072,
},
"meta/muse-spark-1.2-contributor": {
tool_call: true,
reasoning: true,
attachment: true,
limit_context: 1048576,
limit_input: 1048576,
limit_output: 1048576,
},
},
});

const cases: Array<[string, boolean]> = [
["command-code/deepseek/deepseek-v4-flash-max", false], // text-only base
["command-code/deepseek/deepseek-v4-flash-high", false],
["command-code/meta/muse-spark-1.2-contributor-xhigh", true], // vision base
["command-code/meta/muse-spark-1.2-contributor-high", true],
];
for (const [modelId, vision] of cases) {
const caps = getResolvedModelCapabilities(modelId);
assert.equal(caps.provider, "command-code", `${modelId} provider`);
assert.equal(caps.supportsTools, true, `${modelId} must inherit tool support from base`);
assert.equal(caps.supportsVision, vision, `${modelId} must inherit vision from base`);
assert.equal(typeof caps.contextWindow, "number", `${modelId} must inherit a context window`);
assert.equal(caps.supportsThinking, true, `${modelId} must inherit reasoning from base`);
}
});
Loading