diff --git a/CHANGELOG.md b/CHANGELOG.md index 879148f..5804ff3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ ## Unreleased +- Enable image input for models the pinned catalog predates. `src/commandcode-catalog.ts` is generated from a Command Code CLI release, so a model published afterwards is absent from `MODEL_INPUT_MODALITIES` and stays text-only until the next catalog sync: the provider transport publishes `input: ["text"]` to the host and the generate transport refuses image blocks. A new `MODEL_INPUT_MODALITIES_OVERRIDES` hook next to the effort overrides carries the modalities. It re-enables vision for `deepseek/deepseek-v4.1-flash` and `xai/grok-4.6`, both served by the Provider API and both declared `inputModalities:["text","image"]` by `command-code@1.53.0`. + +### Contributors + +- @eibednejo — added the input-modality override hook and the vision entries for the affected models. + ## 0.6.4 - 2026-09-03 - Refresh the generated Command Code capability catalog from `command-code@1.40.1` to `command-code@1.44.0`, adding current image-input, reasoning, effort, and output-limit metadata for newly published models. diff --git a/src/commandcode-catalog-overrides.ts b/src/commandcode-catalog-overrides.ts index cb4accd..1d244b3 100644 --- a/src/commandcode-catalog-overrides.ts +++ b/src/commandcode-catalog-overrides.ts @@ -1,4 +1,4 @@ -import type { CommandCodeReasoningEffort } from "./commandcode-catalog.ts" +import type { CommandCodeInputType, CommandCodeReasoningEffort } from "./commandcode-catalog.ts" /** * Manual reasoning-effort policy for models the official CLI marks as @@ -23,3 +23,26 @@ export const MODEL_EFFORT_OVERRIDES: Readonly< "meta/muse-spark-1.3": ["minimal", "low", "medium", "high", "xhigh"], "meta/muse-spark-1.3-contributor": ["minimal", "low", "medium", "high", "xhigh"], } + +/** + * Manual input-modality overrides for models the generated catalog predates. + * + * Same rationale as the effort overrides above: the generated catalog is pinned + * to one CLI release, so a model published afterwards is absent from + * `MODEL_INPUT_MODALITIES` and falls back to `["text"]`. Both transports honor + * the absence: the provider transport publishes `input: ["text"]` to the host + * and the generate transport refuses image blocks outright. + * + * The Provider API model list carries no modality metadata, so the CLI registry + * is the only source available for this. Add a model only when the CLI registry + * declares `image` for it; remove it once the generated catalog ships the same + * modalities. + */ +export const MODEL_INPUT_MODALITIES_OVERRIDES: Readonly< + Record +> = { + // Served by the Provider API and declared with + // `inputModalities:["text","image"]` by command-code@1.53.0. + "deepseek/deepseek-v4.1-flash": ["text", "image"], + "xai/grok-4.6": ["text", "image"], +} diff --git a/src/models.ts b/src/models.ts index 0d8ffe9..f096105 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1,10 +1,13 @@ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises" import { dirname } from "node:path" -import { MODEL_EFFORT_OVERRIDES } from "./commandcode-catalog-overrides.ts" +import { + MODEL_EFFORT_OVERRIDES, + MODEL_INPUT_MODALITIES_OVERRIDES, +} from "./commandcode-catalog-overrides.ts" import { MODEL_EFFORTS as CATALOG_MODEL_EFFORTS, - MODEL_INPUT_MODALITIES, + MODEL_INPUT_MODALITIES as CATALOG_MODEL_INPUT_MODALITIES, MODEL_MAX_OUTPUT_TOKENS, MODEL_REASONING, type CommandCodeInputType, @@ -17,7 +20,13 @@ export const MODEL_EFFORTS: Readonly> = { + ...CATALOG_MODEL_INPUT_MODALITIES, + ...MODEL_INPUT_MODALITIES_OVERRIDES, +} + +export { MODEL_MAX_OUTPUT_TOKENS, MODEL_REASONING } export type { CommandCodeInputType } export const DEFAULT_PROVIDER_API_BASE = "https://api.commandcode.ai/provider/v1" diff --git a/tests/test-models.ts b/tests/test-models.ts index 821a2e6..545add4 100644 --- a/tests/test-models.ts +++ b/tests/test-models.ts @@ -4,10 +4,14 @@ import { tmpdir } from "node:os" import { join } from "node:path" import { describe, it } from "node:test" -import { MODEL_EFFORT_OVERRIDES } from "../src/commandcode-catalog-overrides.ts" +import { + MODEL_EFFORT_OVERRIDES, + MODEL_INPUT_MODALITIES_OVERRIDES, +} from "../src/commandcode-catalog-overrides.ts" import { COMMAND_CODE_CLI_VERSION, MODEL_EFFORTS as CATALOG_MODEL_EFFORTS, + MODEL_INPUT_MODALITIES as CATALOG_MODEL_INPUT_MODALITIES, } from "../src/commandcode-catalog.ts" import { apiForModelId, @@ -216,6 +220,30 @@ describe("commandCodeModelsFromApiResponse()", () => { } }) + it("merges manual input-modality overrides over the generated catalog", () => { + assert.ok(Object.keys(MODEL_INPUT_MODALITIES_OVERRIDES).length > 0) + for (const [modelId, modalities] of Object.entries(MODEL_INPUT_MODALITIES_OVERRIDES)) { + assert.equal( + CATALOG_MODEL_INPUT_MODALITIES[modelId], + undefined, + `${modelId} now has upstream modalities; drop the manual override`, + ) + assert.ok( + modalities.includes("image"), + `${modelId} override is pointless without image input`, + ) + assert.deepEqual(inputModalitiesForModel(modelId), modalities) + assert.equal(modelSupportsImageInput(modelId), true) + } + for (const [modelId, modalities] of Object.entries(CATALOG_MODEL_INPUT_MODALITIES)) { + assert.deepEqual( + MODEL_INPUT_MODALITIES[modelId], + modalities, + `${modelId} upstream modalities changed`, + ) + } + }) + it("builds separate canonical pi and OMP metadata", () => { for (const [modelId, efforts] of Object.entries(MODEL_EFFORTS)) { const metadata = thinkingMetadataForModel(modelId)