Merge pull request #57 from patlux/fix/model-capabilities-1.32.2
fix(models): refresh Command Code capabilities
This commit is contained in:
@@ -0,0 +1,402 @@
|
||||
import { execFile } from "node:child_process"
|
||||
import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises"
|
||||
import { tmpdir } from "node:os"
|
||||
import { join, resolve } from "node:path"
|
||||
import { pathToFileURL } from "node:url"
|
||||
import { promisify } from "node:util"
|
||||
|
||||
import {
|
||||
COMMAND_CODE_CLI_VERSION,
|
||||
MODEL_EFFORTS,
|
||||
MODEL_INPUT_MODALITIES,
|
||||
} from "../../src/commandcode-catalog.ts"
|
||||
|
||||
const execFileAsync = promisify(execFile)
|
||||
const MODELS_REFERENCE_PATH = "dist/bundled/command-code-knowledge/reference/models.md"
|
||||
const CLI_BUNDLE_PATH = "dist/cli.mjs"
|
||||
const TEXT_ONLY_MARKER = ',__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")'
|
||||
const VALID_EFFORTS = new Set(["minimal", "low", "medium", "high", "xhigh", "max"])
|
||||
const CATALOG_SOURCE_PATH = new URL("../../src/commandcode-catalog.ts", import.meta.url)
|
||||
const README_PATH = new URL("../../README.md", import.meta.url)
|
||||
|
||||
export interface CommandCodeModelMetadata {
|
||||
imageModelIds: readonly string[]
|
||||
reasoningEfforts: Readonly<Record<string, readonly string[]>>
|
||||
}
|
||||
|
||||
export interface ModelMetadataDiff {
|
||||
versionChanged: boolean
|
||||
addedImageModelIds: readonly string[]
|
||||
removedImageModelIds: readonly string[]
|
||||
addedReasoningModelIds: readonly string[]
|
||||
removedReasoningModelIds: readonly string[]
|
||||
changedReasoningModelIds: readonly string[]
|
||||
}
|
||||
|
||||
interface PackedPackage {
|
||||
filename: string
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function isStringArray(value: unknown): value is string[] {
|
||||
return Array.isArray(value) && value.every((entry) => typeof entry === "string")
|
||||
}
|
||||
|
||||
function sorted(values: Iterable<string>): string[] {
|
||||
return [...values].sort((left, right) => left.localeCompare(right))
|
||||
}
|
||||
|
||||
function parsePackedPackage(value: unknown): PackedPackage {
|
||||
if (!Array.isArray(value) || value.length !== 1 || !isRecord(value[0])) {
|
||||
throw new Error("Expected npm pack to return one package")
|
||||
}
|
||||
|
||||
const filename = value[0].filename
|
||||
if (typeof filename !== "string" || filename.length === 0) {
|
||||
throw new Error("Expected npm pack to return a tarball filename")
|
||||
}
|
||||
|
||||
return { filename }
|
||||
}
|
||||
|
||||
export function parsePackageVersion(value: unknown): string {
|
||||
if (typeof value !== "string" || !/^\d+\.\d+\.\d+(?:[-+].+)?$/.test(value)) {
|
||||
throw new Error("Expected npm view to return one semantic version")
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
export function parseModelsReference(markdown: string): {
|
||||
modelIds: readonly string[]
|
||||
reasoningEfforts: Readonly<Record<string, readonly string[]>>
|
||||
} {
|
||||
const modelIds = new Set<string>()
|
||||
const reasoningEfforts: Record<string, readonly string[]> = {}
|
||||
|
||||
for (const line of markdown.split("\n")) {
|
||||
const match = /^\| `([^`]+)` \| [^|]* \| [^|]* \| ([^|]*) \|/.exec(line)
|
||||
if (!match) continue
|
||||
|
||||
const modelId = match[1]
|
||||
const effortsColumn = match[2]?.trim()
|
||||
if (!modelId || !effortsColumn) throw new Error(`Could not parse model row: ${line}`)
|
||||
if (modelIds.has(modelId)) throw new Error(`Duplicate model id in reference: ${modelId}`)
|
||||
modelIds.add(modelId)
|
||||
|
||||
if (effortsColumn === "—") continue
|
||||
|
||||
const efforts = effortsColumn.split(",").map((effort) => effort.trim())
|
||||
if (efforts.length === 0 || efforts.some((effort) => !VALID_EFFORTS.has(effort))) {
|
||||
throw new Error(`Unexpected reasoning efforts for ${modelId}: ${effortsColumn}`)
|
||||
}
|
||||
reasoningEfforts[modelId] = efforts
|
||||
}
|
||||
|
||||
if (modelIds.size === 0) throw new Error("No model rows found in Command Code reference")
|
||||
|
||||
return {
|
||||
modelIds: sorted(modelIds),
|
||||
reasoningEfforts: Object.fromEntries(
|
||||
Object.entries(reasoningEfforts).sort(([left], [right]) => left.localeCompare(right)),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function parseKnownTextOnlyModelIds(bundle: string): readonly string[] {
|
||||
const markerIndex = bundle.indexOf(TEXT_ONLY_MARKER)
|
||||
if (markerIndex < 0) {
|
||||
throw new Error("Could not find Command Code's isKnownTextOnlyModel catalog")
|
||||
}
|
||||
|
||||
const setStart = bundle.lastIndexOf("new Set([", markerIndex)
|
||||
if (setStart < 0) throw new Error("Could not find the text-only model set")
|
||||
|
||||
const arrayStart = setStart + "new Set(".length
|
||||
const arrayEnd = markerIndex - 1
|
||||
const literal = bundle.slice(arrayStart, arrayEnd)
|
||||
const parsed: unknown = JSON.parse(literal)
|
||||
if (!isStringArray(parsed)) throw new Error("Expected the text-only model catalog to be strings")
|
||||
|
||||
return sorted(new Set(parsed))
|
||||
}
|
||||
|
||||
export function commandCodeModelMetadataFromContents(
|
||||
modelsReference: string,
|
||||
cliBundle: string,
|
||||
): CommandCodeModelMetadata {
|
||||
const reference = parseModelsReference(modelsReference)
|
||||
const textOnlyModelIds = new Set(parseKnownTextOnlyModelIds(cliBundle))
|
||||
|
||||
return {
|
||||
imageModelIds: reference.modelIds.filter((modelId) => !textOnlyModelIds.has(modelId)),
|
||||
reasoningEfforts: reference.reasoningEfforts,
|
||||
}
|
||||
}
|
||||
|
||||
export function currentModelMetadata(): CommandCodeModelMetadata {
|
||||
return {
|
||||
imageModelIds: sorted(Object.keys(MODEL_INPUT_MODALITIES)),
|
||||
reasoningEfforts: Object.fromEntries(
|
||||
Object.entries(MODEL_EFFORTS)
|
||||
.sort(([left], [right]) => left.localeCompare(right))
|
||||
.map(([modelId, efforts]) => [modelId, [...efforts]]),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function diffModelMetadata(
|
||||
current: CommandCodeModelMetadata,
|
||||
upstream: CommandCodeModelMetadata,
|
||||
currentVersion = COMMAND_CODE_CLI_VERSION,
|
||||
upstreamVersion = COMMAND_CODE_CLI_VERSION,
|
||||
): ModelMetadataDiff {
|
||||
const currentImages = new Set(current.imageModelIds)
|
||||
const upstreamImages = new Set(upstream.imageModelIds)
|
||||
const currentReasoningIds = Object.keys(current.reasoningEfforts)
|
||||
const upstreamReasoningIds = Object.keys(upstream.reasoningEfforts)
|
||||
const currentReasoningSet = new Set(currentReasoningIds)
|
||||
const upstreamReasoningSet = new Set(upstreamReasoningIds)
|
||||
|
||||
return {
|
||||
versionChanged: currentVersion !== upstreamVersion,
|
||||
addedImageModelIds: sorted(
|
||||
upstream.imageModelIds.filter((modelId) => !currentImages.has(modelId)),
|
||||
),
|
||||
removedImageModelIds: sorted(
|
||||
current.imageModelIds.filter((modelId) => !upstreamImages.has(modelId)),
|
||||
),
|
||||
addedReasoningModelIds: sorted(
|
||||
upstreamReasoningIds.filter((modelId) => !currentReasoningSet.has(modelId)),
|
||||
),
|
||||
removedReasoningModelIds: sorted(
|
||||
currentReasoningIds.filter((modelId) => !upstreamReasoningSet.has(modelId)),
|
||||
),
|
||||
changedReasoningModelIds: sorted(
|
||||
upstreamReasoningIds.filter(
|
||||
(modelId) =>
|
||||
currentReasoningSet.has(modelId) &&
|
||||
JSON.stringify(current.reasoningEfforts[modelId]) !==
|
||||
JSON.stringify(upstream.reasoningEfforts[modelId]),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
export function hasModelMetadataDiff(diff: ModelMetadataDiff): boolean {
|
||||
return (
|
||||
diff.versionChanged ||
|
||||
Object.entries(diff).some(([key, modelIds]) => key !== "versionChanged" && modelIds.length > 0)
|
||||
)
|
||||
}
|
||||
|
||||
function formatList(modelIds: readonly string[]): string {
|
||||
return modelIds.length > 0 ? modelIds.map((modelId) => `\`${modelId}\``).join(", ") : "None"
|
||||
}
|
||||
|
||||
function formatReasoningChanges(
|
||||
modelIds: readonly string[],
|
||||
current: CommandCodeModelMetadata,
|
||||
upstream: CommandCodeModelMetadata,
|
||||
): string {
|
||||
if (modelIds.length === 0) return "None"
|
||||
return modelIds
|
||||
.map(
|
||||
(modelId) =>
|
||||
`\`${modelId}\`: \`${(current.reasoningEfforts[modelId] ?? []).join(", ")}\` → \`${(
|
||||
upstream.reasoningEfforts[modelId] ?? []
|
||||
).join(", ")}\``,
|
||||
)
|
||||
.join("<br>")
|
||||
}
|
||||
|
||||
function quoted(value: string): string {
|
||||
return JSON.stringify(value)
|
||||
}
|
||||
|
||||
function recordEntries(
|
||||
values: Readonly<Record<string, readonly string[]>>,
|
||||
): readonly [string, readonly string[]][] {
|
||||
return Object.entries(values).sort(([left], [right]) => left.localeCompare(right))
|
||||
}
|
||||
|
||||
export function renderCommandCodeCatalog(
|
||||
packageVersion: string,
|
||||
metadata: CommandCodeModelMetadata,
|
||||
): string {
|
||||
const imageEntries = sorted(metadata.imageModelIds)
|
||||
.map((modelId) => ` ${quoted(modelId)}: ["text", "image"],`)
|
||||
.join("\n")
|
||||
const reasoningEntries = recordEntries(metadata.reasoningEfforts)
|
||||
.map(
|
||||
([modelId, efforts]) =>
|
||||
` ${quoted(modelId)}: [${efforts.map((effort) => quoted(effort)).join(", ")}],`,
|
||||
)
|
||||
.join("\n")
|
||||
|
||||
return `export const COMMAND_CODE_CLI_VERSION = ${quoted(packageVersion)}\n\nexport type CommandCodeInputType = "text" | "image"\nexport type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"\n\n/**\n * Generated from command-code@${packageVersion} by \`npm run sync:commandcode-catalog\`.\n * Do not edit manually.\n */\nexport const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {\n${imageEntries}\n}\n\nexport const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {\n${reasoningEntries}\n}\n`
|
||||
}
|
||||
|
||||
function updateDocumentedCatalogVersion(
|
||||
contents: string,
|
||||
packageVersion: string,
|
||||
context: string,
|
||||
): string {
|
||||
const pattern = /command-code@\d+\.\d+\.\d+(?:[-+][^`\s,]+)?/
|
||||
if (!pattern.test(contents)) throw new Error(`Could not find the ${context} catalog version`)
|
||||
return contents.replace(pattern, `command-code@${packageVersion}`)
|
||||
}
|
||||
|
||||
export function updateReadmeCatalogVersion(readme: string, packageVersion: string): string {
|
||||
return updateDocumentedCatalogVersion(readme, packageVersion, "README")
|
||||
}
|
||||
|
||||
async function writeSynchronizedCatalog(
|
||||
packageVersion: string,
|
||||
metadata: CommandCodeModelMetadata,
|
||||
): Promise<void> {
|
||||
const readme = await readFile(README_PATH, "utf-8")
|
||||
await Promise.all([
|
||||
writeFile(CATALOG_SOURCE_PATH, renderCommandCodeCatalog(packageVersion, metadata), "utf-8"),
|
||||
writeFile(README_PATH, updateReadmeCatalogVersion(readme, packageVersion), "utf-8"),
|
||||
])
|
||||
}
|
||||
|
||||
function metadataReport(
|
||||
packageVersion: string,
|
||||
current: CommandCodeModelMetadata,
|
||||
upstream: CommandCodeModelMetadata,
|
||||
diff: ModelMetadataDiff,
|
||||
): string {
|
||||
const status = hasModelMetadataDiff(diff) ? "❌ Drift detected" : "✅ Metadata is current"
|
||||
return [
|
||||
"## Command Code static model metadata",
|
||||
"",
|
||||
`**${status}**`,
|
||||
"",
|
||||
`- Repository snapshot: \`command-code@${COMMAND_CODE_CLI_VERSION}\``,
|
||||
`- Inspected package: \`command-code@${packageVersion}\``,
|
||||
`- Image-capable models: ${current.imageModelIds.length} repository / ${upstream.imageModelIds.length} upstream`,
|
||||
`- Reasoning models: ${Object.keys(current.reasoningEfforts).length} repository / ${Object.keys(upstream.reasoningEfforts).length} upstream`,
|
||||
"",
|
||||
"| Change | Models |",
|
||||
"| --- | --- |",
|
||||
`| CLI version | ${diff.versionChanged ? `\`${COMMAND_CODE_CLI_VERSION}\` → \`${packageVersion}\`` : "Current"} |`,
|
||||
`| New image support | ${formatList(diff.addedImageModelIds)} |`,
|
||||
`| Removed image support | ${formatList(diff.removedImageModelIds)} |`,
|
||||
`| New reasoning metadata | ${formatList(diff.addedReasoningModelIds)} |`,
|
||||
`| Removed reasoning metadata | ${formatList(diff.removedReasoningModelIds)} |`,
|
||||
`| Changed reasoning efforts | ${formatReasoningChanges(diff.changedReasoningModelIds, current, upstream)} |`,
|
||||
"",
|
||||
].join("\n")
|
||||
}
|
||||
|
||||
async function resolvePackageSpec(
|
||||
packageSpec: string,
|
||||
directory: string,
|
||||
npmCacheDirectory: string,
|
||||
): Promise<string> {
|
||||
if (packageSpec !== "command-code@latest") return packageSpec
|
||||
|
||||
const { stdout } = await execFileAsync(
|
||||
"npm",
|
||||
["view", packageSpec, "version", "--json", "--prefer-online", "--cache", npmCacheDirectory],
|
||||
{
|
||||
cwd: directory,
|
||||
encoding: "utf-8",
|
||||
},
|
||||
)
|
||||
return `command-code@${parsePackageVersion(JSON.parse(stdout) as unknown)}`
|
||||
}
|
||||
|
||||
async function inspectPackedPackage(packageSpec: string): Promise<{
|
||||
packageVersion: string
|
||||
metadata: CommandCodeModelMetadata
|
||||
}> {
|
||||
const directory = await mkdtemp(join(tmpdir(), "pi-commandcode-model-check-"))
|
||||
const npmCacheDirectory = join(directory, "npm-cache")
|
||||
|
||||
try {
|
||||
const resolvedPackageSpec = await resolvePackageSpec(packageSpec, directory, npmCacheDirectory)
|
||||
const { stdout } = await execFileAsync(
|
||||
"npm",
|
||||
["pack", resolvedPackageSpec, "--json", "--prefer-online", "--cache", npmCacheDirectory],
|
||||
{
|
||||
cwd: directory,
|
||||
encoding: "utf-8",
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
},
|
||||
)
|
||||
const packed = parsePackedPackage(JSON.parse(stdout) as unknown)
|
||||
await execFileAsync("tar", ["-xzf", packed.filename], { cwd: directory })
|
||||
|
||||
const packageDirectory = join(directory, "package")
|
||||
const packageJsonContents = await readFile(join(packageDirectory, "package.json"), "utf-8")
|
||||
const packageJson: unknown = JSON.parse(packageJsonContents)
|
||||
if (!isRecord(packageJson) || typeof packageJson.version !== "string") {
|
||||
throw new Error("Expected command-code package.json to contain a version")
|
||||
}
|
||||
|
||||
const [modelsReference, cliBundle] = await Promise.all([
|
||||
readFile(join(packageDirectory, MODELS_REFERENCE_PATH), "utf-8"),
|
||||
readFile(join(packageDirectory, CLI_BUNDLE_PATH), "utf-8"),
|
||||
])
|
||||
|
||||
return {
|
||||
packageVersion: packageJson.version,
|
||||
metadata: commandCodeModelMetadataFromContents(modelsReference, cliBundle),
|
||||
}
|
||||
} finally {
|
||||
await rm(directory, { recursive: true, force: true })
|
||||
}
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
const write = process.argv.includes("--write")
|
||||
const packageSpec =
|
||||
process.argv.find((argument) => argument.startsWith("command-code@")) ?? "command-code@latest"
|
||||
const current = currentModelMetadata()
|
||||
const upstreamPackage = await inspectPackedPackage(packageSpec)
|
||||
const diff = diffModelMetadata(
|
||||
current,
|
||||
upstreamPackage.metadata,
|
||||
COMMAND_CODE_CLI_VERSION,
|
||||
upstreamPackage.packageVersion,
|
||||
)
|
||||
const report = metadataReport(
|
||||
upstreamPackage.packageVersion,
|
||||
current,
|
||||
upstreamPackage.metadata,
|
||||
diff,
|
||||
)
|
||||
|
||||
console.log(report)
|
||||
|
||||
if (write) {
|
||||
await writeSynchronizedCatalog(upstreamPackage.packageVersion, upstreamPackage.metadata)
|
||||
console.log(`Synchronized static metadata with command-code@${upstreamPackage.packageVersion}.`)
|
||||
return
|
||||
}
|
||||
|
||||
if (hasModelMetadataDiff(diff)) {
|
||||
throw new Error(
|
||||
`Static model metadata differs from command-code@${upstreamPackage.packageVersion}. Update src/models.ts and the snapshot version.`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function isMainModule(): boolean {
|
||||
const entrypoint = process.argv[1]
|
||||
return entrypoint !== undefined && pathToFileURL(resolve(entrypoint)).href === import.meta.url
|
||||
}
|
||||
|
||||
if (isMainModule()) {
|
||||
try {
|
||||
await main()
|
||||
} catch (error) {
|
||||
console.error(error instanceof Error ? error.message : String(error))
|
||||
process.exitCode = 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
name: Command Code catalog sync
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- ".github/scripts/check-commandcode-model-metadata.ts"
|
||||
- ".github/workflows/model-metadata.yml"
|
||||
- "src/commandcode-catalog.ts"
|
||||
- "src/core.ts"
|
||||
- "src/models.ts"
|
||||
- "tests/test-model-metadata-check.ts"
|
||||
schedule:
|
||||
- cron: "17 6 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: commandcode-catalog-${{ github.event_name == 'pull_request' && github.event.pull_request.number || 'sync' }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
registry-url: https://registry.npmjs.org
|
||||
- run: npm ci
|
||||
- name: Compare with the latest Command Code CLI
|
||||
run: npm run check:commandcode-catalog | tee commandcode-catalog-report.md
|
||||
- name: Publish catalog report
|
||||
if: always()
|
||||
run: cat commandcode-catalog-report.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
sync:
|
||||
if: github.event_name != 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: npm
|
||||
registry-url: https://registry.npmjs.org
|
||||
- run: npm ci
|
||||
- name: Synchronize with the latest Command Code CLI
|
||||
run: npm run sync:commandcode-catalog | tee commandcode-catalog-report.md
|
||||
- name: Format and verify synchronized files
|
||||
run: |
|
||||
npm run format -- src/commandcode-catalog.ts README.md
|
||||
npm run typecheck
|
||||
npm run test:models
|
||||
npm run format:check
|
||||
git diff --check
|
||||
- name: Publish catalog report
|
||||
if: always()
|
||||
run: cat commandcode-catalog-report.md >> "$GITHUB_STEP_SUMMARY"
|
||||
- name: Create or update synchronization PR
|
||||
uses: peter-evans/create-pull-request@v8
|
||||
with:
|
||||
branch: automation/commandcode-catalog
|
||||
delete-branch: true
|
||||
commit-message: "chore(models): sync Command Code catalog"
|
||||
title: "chore(models): sync Command Code catalog"
|
||||
body: |
|
||||
Automated synchronization with the latest published `command-code` CLI package.
|
||||
|
||||
This updates only machine-readable compatibility metadata:
|
||||
- CLI version used in the `x-command-code-version` header
|
||||
- image-input capabilities
|
||||
- supported reasoning efforts
|
||||
- documented catalog snapshot version
|
||||
|
||||
Pricing remains review-only because CLI documentation does not represent every pricing tier and temporary promotion used by the provider.
|
||||
assignees: patlux
|
||||
add-paths: |
|
||||
src/commandcode-catalog.ts
|
||||
README.md
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Add a daily GitHub Actions synchronization job that opens or updates a pull request for CLI version, image capability, and reasoning-effort changes in the latest published Command Code catalog.
|
||||
- Refresh static model capabilities from `command-code@1.32.2`, including new image and reasoning metadata.
|
||||
- Add `/commandcode-quota` with live credits, plan, usage totals, and rolling-limit diagnostics from Command Code's alpha usage endpoints.
|
||||
- Add `zai-org/GLM-5.3` with its verified reasoning efforts and display pricing.
|
||||
- Prefer Command Code's Provider API (`/provider/v1/chat/completions` and `/provider/v1/messages`) and automatically fall back to the existing `/alpha/generate` transport only when the Provider API returns `403 upgrade_required` for a Go-plan account.
|
||||
|
||||
@@ -144,7 +144,7 @@ The following environment variables are intended for tests, local mocks, and com
|
||||
|
||||
## Image input
|
||||
|
||||
The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.1`; unknown models default to text-only until their upstream metadata is reviewed.
|
||||
The provider advertises image input only for models marked with the `image` input modality in the official Command Code CLI model catalog. The capability snapshot currently follows `command-code@1.32.2`; unknown models default to text-only until their upstream metadata is reviewed. A daily GitHub Actions job synchronizes the CLI version, image capabilities, and reasoning efforts with the latest published CLI package and opens or updates a reviewable pull request when they change. Pricing remains manually reviewed because the CLI catalog does not expose every pricing tier and temporary promotion used by the provider.
|
||||
|
||||
For vision-capable models, Pi's native provider adapters forward image blocks from user messages and tool results using the documented OpenAI or Anthropic message schema. Unknown and text-only models remain marked text-only in Pi.
|
||||
|
||||
|
||||
+5
-3
@@ -29,16 +29,18 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "npm run typecheck && tsx tests/test-package-manifest.ts && tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts && node tests/test-pi-isolated.mjs && node tests/test-pi-authenticated.mjs && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs",
|
||||
"test": "npm run typecheck && tsx tests/test-package-manifest.ts && tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts && node tests/test-pi-isolated.mjs && node tests/test-pi-authenticated.mjs && node tests/test-pi-local.mjs && node tests/test-omp-compat.mjs",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"format:check": "prettier --check '**/*.{ts,mjs,json,md}'",
|
||||
"format": "prettier --write '**/*.{ts,mjs,json,md}'",
|
||||
"pi:isolated": "node scripts/pi-isolated.mjs",
|
||||
"pi:authenticated": "node scripts/pi-authenticated.mjs",
|
||||
"check:commandcode-catalog": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest",
|
||||
"sync:commandcode-catalog": "tsx .github/scripts/check-commandcode-model-metadata.ts command-code@latest --write",
|
||||
"test:quota": "tsx tests/test-quota.ts && tsx tests/test-quota-command.ts",
|
||||
"test:unit": "tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts",
|
||||
"test:unit": "tsx tests/test-api-key.ts && tsx tests/test-pure-functions.ts && tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts && tsx tests/test-runtime.ts && tsx tests/test-pricing.ts && tsx tests/test-cost.ts && tsx tests/test-oauth.ts && tsx tests/test-abort.ts && tsx tests/test-overflow.ts && tsx tests/test-stream.ts && tsx tests/test-quota.ts && tsx tests/test-quota-command.ts && tsx tests/test-retry.ts && tsx tests/test-transport.ts",
|
||||
"test:api-key": "tsx tests/test-api-key.ts",
|
||||
"test:models": "tsx tests/test-models.ts",
|
||||
"test:models": "tsx tests/test-models.ts && tsx tests/test-model-metadata-check.ts",
|
||||
"test:runtime": "tsx tests/test-runtime.ts",
|
||||
"test:pricing": "tsx tests/test-pricing.ts",
|
||||
"test:oauth": "tsx tests/test-oauth.ts",
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
export const COMMAND_CODE_CLI_VERSION = "1.32.2"
|
||||
|
||||
export type CommandCodeInputType = "text" | "image"
|
||||
export type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
|
||||
|
||||
/**
|
||||
* Generated from command-code@1.32.2 by `npm run sync:commandcode-catalog`.
|
||||
* Do not edit manually.
|
||||
*/
|
||||
export const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {
|
||||
"claude-fable-5": ["text", "image"],
|
||||
"claude-haiku-4-5-20251001": ["text", "image"],
|
||||
"claude-opus-4-7": ["text", "image"],
|
||||
"claude-opus-4-8": ["text", "image"],
|
||||
"claude-opus-5": ["text", "image"],
|
||||
"claude-sonnet-4-6": ["text", "image"],
|
||||
"claude-sonnet-5": ["text", "image"],
|
||||
"deepseek/deepseek-v4-flash-vision-exp": ["text", "image"],
|
||||
"google/gemini-3.1-flash-lite": ["text", "image"],
|
||||
"google/gemini-3.5-flash": ["text", "image"],
|
||||
"google/gemini-3.5-flash-lite": ["text", "image"],
|
||||
"google/gemini-3.6-flash": ["text", "image"],
|
||||
"google/gemini-3.7-flash": ["text", "image"],
|
||||
"gpt-5.3-codex": ["text", "image"],
|
||||
"gpt-5.4": ["text", "image"],
|
||||
"gpt-5.4-mini": ["text", "image"],
|
||||
"gpt-5.5": ["text", "image"],
|
||||
"gpt-5.6-luna": ["text", "image"],
|
||||
"gpt-5.6-sol": ["text", "image"],
|
||||
"gpt-5.6-terra": ["text", "image"],
|
||||
"meta/muse-spark-1.1": ["text", "image"],
|
||||
"meta/muse-spark-1.2": ["text", "image"],
|
||||
"meta/muse-spark-1.2-contributor": ["text", "image"],
|
||||
"MiniMaxAI/MiniMax-M3": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.5": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.6": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.7-Code": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"],
|
||||
"moonshotai/Kimi-K3": ["text", "image"],
|
||||
"Qwen/Qwen3.6-Plus": ["text", "image"],
|
||||
"Qwen/Qwen3.7-Flash": ["text", "image"],
|
||||
"Qwen/Qwen3.7-Plus": ["text", "image"],
|
||||
"Qwen/Qwen3.8-27B": ["text", "image"],
|
||||
"Qwen/Qwen3.8-Max": ["text", "image"],
|
||||
"sakana/fugu-ultra": ["text", "image"],
|
||||
"stealth/ox-alpha": ["text", "image"],
|
||||
"stepfun/Step-3.7-Flash": ["text", "image"],
|
||||
"thinkingmachines/inkling": ["text", "image"],
|
||||
"thinkingmachines/inkling-small": ["text", "image"],
|
||||
"xai/grok-4.5": ["text", "image"],
|
||||
"xiaomi/mimo-v2.5": ["text", "image"],
|
||||
}
|
||||
|
||||
export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {
|
||||
"claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"deepseek/deepseek-v4-flash": ["high", "max"],
|
||||
"deepseek/deepseek-v4-flash-vision-exp": ["high", "max"],
|
||||
"deepseek/deepseek-v4-pro": ["high", "max"],
|
||||
"google/gemini-3.1-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.6-flash": ["low", "medium", "high"],
|
||||
"google/gemini-3.7-flash": ["low", "medium", "high"],
|
||||
"gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4-mini": ["low", "medium", "high"],
|
||||
"gpt-5.5": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
|
||||
"Qwen/Qwen3.8-27B": ["low", "medium", "xhigh"],
|
||||
"Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"],
|
||||
"sakana/fugu-ultra": ["high", "xhigh"],
|
||||
"stealth/ox-alpha": ["low", "high", "max"],
|
||||
"xai/grok-4.5": ["low", "medium", "high"],
|
||||
"xai/grok-4.6": ["low", "medium", "high", "xhigh"],
|
||||
"zai-org/GLM-5.2": ["high", "max"],
|
||||
"zai-org/GLM-5.3": ["low", "high", "max"],
|
||||
}
|
||||
+2
-1
@@ -7,6 +7,7 @@
|
||||
|
||||
import { randomUUID } from "node:crypto"
|
||||
|
||||
import { COMMAND_CODE_CLI_VERSION } from "./commandcode-catalog.ts"
|
||||
import { commandCodeErrorMessage, redactCommandCodeErrorText } from "./overflow.ts"
|
||||
import { modelSupportsImageInput } from "./models.ts"
|
||||
import {
|
||||
@@ -43,7 +44,7 @@ export * from "./overflow.ts"
|
||||
export * from "./types.ts"
|
||||
|
||||
export const DEFAULT_API_BASE = "https://api.commandcode.ai"
|
||||
export const COMMAND_CODE_CLI_VERSION = "1.32.1"
|
||||
export { COMMAND_CODE_CLI_VERSION }
|
||||
|
||||
const DEFAULT_GENERATE_MAX_TOKENS = 64_000
|
||||
const DEFAULT_MAX_RETRIES = 0
|
||||
|
||||
+10
-85
@@ -1,6 +1,16 @@
|
||||
import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"
|
||||
import { dirname } from "node:path"
|
||||
|
||||
import {
|
||||
MODEL_EFFORTS,
|
||||
MODEL_INPUT_MODALITIES,
|
||||
type CommandCodeInputType,
|
||||
type CommandCodeReasoningEffort,
|
||||
} from "./commandcode-catalog.ts"
|
||||
|
||||
export { MODEL_EFFORTS, MODEL_INPUT_MODALITIES }
|
||||
export type { CommandCodeInputType }
|
||||
|
||||
export const DEFAULT_PROVIDER_API_BASE = "https://api.commandcode.ai/provider/v1"
|
||||
export const DEFAULT_MODELS_URL = `${DEFAULT_PROVIDER_API_BASE}/models`
|
||||
export const DEFAULT_MODELS_TIMEOUT_MS = 10_000
|
||||
@@ -9,53 +19,6 @@ const DEFAULT_MAX_OUTPUT_TOKENS = 65_536
|
||||
const MODEL_CACHE_VERSION = 1
|
||||
|
||||
export type CommandCodeApi = "openai-completions" | "anthropic-messages"
|
||||
export type CommandCodeInputType = "text" | "image"
|
||||
|
||||
/**
|
||||
* Model input modalities from the command-code@1.32.1 bundled catalog.
|
||||
* Models omitted here remain text-only so newly discovered IDs never claim
|
||||
* image support without upstream evidence.
|
||||
*/
|
||||
export const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {
|
||||
"MiniMaxAI/MiniMax-M3": ["text", "image"],
|
||||
"Qwen/Qwen3.6-Plus": ["text", "image"],
|
||||
"Qwen/Qwen3.7-Flash": ["text", "image"],
|
||||
"Qwen/Qwen3.7-Plus": ["text", "image"],
|
||||
"Qwen/Qwen3.8-Max": ["text", "image"],
|
||||
"claude-fable-5": ["text", "image"],
|
||||
"claude-haiku-4-5-20251001": ["text", "image"],
|
||||
"claude-opus-4-7": ["text", "image"],
|
||||
"claude-opus-4-8": ["text", "image"],
|
||||
"claude-opus-5": ["text", "image"],
|
||||
"claude-sonnet-4-6": ["text", "image"],
|
||||
"claude-sonnet-5": ["text", "image"],
|
||||
"google/gemini-3.1-flash-lite": ["text", "image"],
|
||||
"google/gemini-3.5-flash": ["text", "image"],
|
||||
"google/gemini-3.5-flash-lite": ["text", "image"],
|
||||
"google/gemini-3.6-flash": ["text", "image"],
|
||||
"gpt-5.3-codex": ["text", "image"],
|
||||
"gpt-5.4": ["text", "image"],
|
||||
"gpt-5.4-mini": ["text", "image"],
|
||||
"gpt-5.5": ["text", "image"],
|
||||
"gpt-5.6-luna": ["text", "image"],
|
||||
"gpt-5.6-sol": ["text", "image"],
|
||||
"gpt-5.6-terra": ["text", "image"],
|
||||
"meta/muse-spark-1.1": ["text", "image"],
|
||||
"meta/muse-spark-1.2": ["text", "image"],
|
||||
"meta/muse-spark-1.2-contributor": ["text", "image"],
|
||||
"deepseek/deepseek-v4-flash-vision-exp": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.5": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.6": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.7-Code": ["text", "image"],
|
||||
"moonshotai/Kimi-K2.7-Code-Highspeed": ["text", "image"],
|
||||
"moonshotai/Kimi-K3": ["text", "image"],
|
||||
"sakana/fugu-ultra": ["text", "image"],
|
||||
"stepfun/Step-3.7-Flash": ["text", "image"],
|
||||
"thinkingmachines/inkling": ["text", "image"],
|
||||
"thinkingmachines/inkling-small": ["text", "image"],
|
||||
"xai/grok-4.5": ["text", "image"],
|
||||
"xiaomi/mimo-v2.5": ["text", "image"],
|
||||
}
|
||||
|
||||
const TEXT_INPUT_ONLY = ["text"] as const
|
||||
|
||||
@@ -69,44 +32,6 @@ export function modelSupportsImageInput(modelId: string): boolean {
|
||||
|
||||
export type PiThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
|
||||
|
||||
type CommandCodeReasoningEffort = Exclude<PiThinkingLevel, "off">
|
||||
|
||||
/**
|
||||
* Per-model reasoning efforts supported by Command Code's generate endpoint.
|
||||
*
|
||||
* The Provider API does not expose reasoning metadata. This is an exact
|
||||
* snapshot of `reasoningEfforts` from the command-code@1.32.1 model catalog
|
||||
* (`packages/shared/src/model-catalog.ts`, also published in the generated
|
||||
* `dist/bundled/command-code-knowledge/reference/models.md`). Models omitted
|
||||
* here let Command Code choose their reasoning depth, matching the CLI.
|
||||
*/
|
||||
export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {
|
||||
"Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"],
|
||||
"claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"deepseek/deepseek-v4-flash": ["high", "max"],
|
||||
"deepseek/deepseek-v4-pro": ["high", "max"],
|
||||
"gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4-mini": ["low", "medium", "high"],
|
||||
"gpt-5.5": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
|
||||
"google/gemini-3.1-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.6-flash": ["low", "medium", "high"],
|
||||
"sakana/fugu-ultra": ["high", "xhigh"],
|
||||
"xai/grok-4.5": ["low", "medium", "high"],
|
||||
"zai-org/GLM-5.2": ["high", "max"],
|
||||
"zai-org/GLM-5.3": ["low", "high", "max"],
|
||||
}
|
||||
|
||||
const PI_THINKING_LEVELS: readonly PiThinkingLevel[] = [
|
||||
"off",
|
||||
"minimal",
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { describe, it } from "node:test"
|
||||
|
||||
import {
|
||||
commandCodeModelMetadataFromContents,
|
||||
diffModelMetadata,
|
||||
hasModelMetadataDiff,
|
||||
parseKnownTextOnlyModelIds,
|
||||
parseModelsReference,
|
||||
parsePackageVersion,
|
||||
renderCommandCodeCatalog,
|
||||
updateReadmeCatalogVersion,
|
||||
type CommandCodeModelMetadata,
|
||||
} from "../.github/scripts/check-commandcode-model-metadata.ts"
|
||||
|
||||
const MODELS_REFERENCE = `
|
||||
| Id (use EXACTLY this) | Name | Context | Efforts | $/1M in/out · cache read | Min plan | Best for |
|
||||
|---|---|---|---|---|---|---|
|
||||
| \`vision-model\` | Vision | 1M | low, high | $1/$2 | Go | images |
|
||||
| \`text-model\` | Text | 200K | — | $1/$2 | Go | text |
|
||||
`
|
||||
|
||||
const CLI_BUNDLE =
|
||||
'const catalog=new Set(["text-model"]),__name(isKnownTextOnlyModel,"isKnownTextOnlyModel")'
|
||||
|
||||
describe("Command Code model metadata checker", () => {
|
||||
it("parses model ids and reasoning efforts from the generated reference", () => {
|
||||
assert.deepEqual(parseModelsReference(MODELS_REFERENCE), {
|
||||
modelIds: ["text-model", "vision-model"],
|
||||
reasoningEfforts: { "vision-model": ["low", "high"] },
|
||||
})
|
||||
})
|
||||
|
||||
it("extracts the text-only set from the bundled CLI catalog", () => {
|
||||
assert.deepEqual(parseKnownTextOnlyModelIds(CLI_BUNDLE), ["text-model"])
|
||||
})
|
||||
|
||||
it("accepts one exact npm registry version and rejects stale-looking output shapes", () => {
|
||||
assert.equal(parsePackageVersion("1.32.2"), "1.32.2")
|
||||
assert.equal(parsePackageVersion("2.0.0-beta.1"), "2.0.0-beta.1")
|
||||
assert.throws(() => parsePackageVersion(["1.32.1", "1.32.2"]), /one semantic version/)
|
||||
assert.throws(() => parsePackageVersion("latest"), /one semantic version/)
|
||||
})
|
||||
|
||||
it("derives image support by excluding known text-only models", () => {
|
||||
assert.deepEqual(commandCodeModelMetadataFromContents(MODELS_REFERENCE, CLI_BUNDLE), {
|
||||
imageModelIds: ["vision-model"],
|
||||
reasoningEfforts: { "vision-model": ["low", "high"] },
|
||||
})
|
||||
})
|
||||
|
||||
it("reports additions, removals, and changed reasoning efforts", () => {
|
||||
const current: CommandCodeModelMetadata = {
|
||||
imageModelIds: ["removed-image", "stable-image"],
|
||||
reasoningEfforts: {
|
||||
"changed-reasoning": ["low"],
|
||||
"removed-reasoning": ["high"],
|
||||
"stable-reasoning": ["low", "high"],
|
||||
},
|
||||
}
|
||||
const upstream: CommandCodeModelMetadata = {
|
||||
imageModelIds: ["added-image", "stable-image"],
|
||||
reasoningEfforts: {
|
||||
"added-reasoning": ["max"],
|
||||
"changed-reasoning": ["low", "high"],
|
||||
"stable-reasoning": ["low", "high"],
|
||||
},
|
||||
}
|
||||
|
||||
const diff = diffModelMetadata(current, upstream)
|
||||
|
||||
assert.deepEqual(diff, {
|
||||
versionChanged: false,
|
||||
addedImageModelIds: ["added-image"],
|
||||
removedImageModelIds: ["removed-image"],
|
||||
addedReasoningModelIds: ["added-reasoning"],
|
||||
removedReasoningModelIds: ["removed-reasoning"],
|
||||
changedReasoningModelIds: ["changed-reasoning"],
|
||||
})
|
||||
assert.equal(hasModelMetadataDiff(diff), true)
|
||||
})
|
||||
|
||||
it("reports CLI version drift even when model metadata is unchanged", () => {
|
||||
const metadata: CommandCodeModelMetadata = {
|
||||
imageModelIds: ["vision-model"],
|
||||
reasoningEfforts: { "vision-model": ["low"] },
|
||||
}
|
||||
|
||||
const diff = diffModelMetadata(metadata, metadata, "1.32.2", "1.33.0")
|
||||
|
||||
assert.equal(diff.versionChanged, true)
|
||||
assert.equal(hasModelMetadataDiff(diff), true)
|
||||
})
|
||||
|
||||
it("renders a deterministic generated catalog and updates the README version", () => {
|
||||
assert.equal(
|
||||
renderCommandCodeCatalog("1.33.0", {
|
||||
imageModelIds: ["b-model", "a-model"],
|
||||
reasoningEfforts: {
|
||||
"b-model": ["high", "max"],
|
||||
"a-model": ["low"],
|
||||
},
|
||||
}),
|
||||
`export const COMMAND_CODE_CLI_VERSION = "1.33.0"
|
||||
|
||||
export type CommandCodeInputType = "text" | "image"
|
||||
export type CommandCodeReasoningEffort = "minimal" | "low" | "medium" | "high" | "xhigh" | "max"
|
||||
|
||||
/**
|
||||
* Generated from command-code@1.33.0 by \`npm run sync:commandcode-catalog\`.
|
||||
* Do not edit manually.
|
||||
*/
|
||||
export const MODEL_INPUT_MODALITIES: Readonly<Record<string, readonly CommandCodeInputType[]>> = {
|
||||
"a-model": ["text", "image"],
|
||||
"b-model": ["text", "image"],
|
||||
}
|
||||
|
||||
export const MODEL_EFFORTS: Readonly<Record<string, readonly CommandCodeReasoningEffort[]>> = {
|
||||
"a-model": ["low"],
|
||||
"b-model": ["high", "max"],
|
||||
}
|
||||
`,
|
||||
)
|
||||
assert.equal(
|
||||
updateReadmeCatalogVersion(
|
||||
"The capability snapshot currently follows `command-code@1.32.2`.",
|
||||
"1.33.0",
|
||||
),
|
||||
"The capability snapshot currently follows `command-code@1.33.0`.",
|
||||
)
|
||||
})
|
||||
|
||||
it("rejects unexpected upstream structures instead of silently passing", () => {
|
||||
assert.throws(() => parseModelsReference("# no catalog"), /No model rows/)
|
||||
assert.throws(
|
||||
() => parseModelsReference(MODELS_REFERENCE.replace("low, high", "low, turbo")),
|
||||
/Unexpected reasoning efforts/,
|
||||
)
|
||||
assert.throws(() => parseKnownTextOnlyModelIds("const unrelated = true"), /Could not find/)
|
||||
})
|
||||
})
|
||||
+19
-29
@@ -4,6 +4,7 @@ import { tmpdir } from "node:os"
|
||||
import { join } from "node:path"
|
||||
import { describe, it } from "node:test"
|
||||
|
||||
import { COMMAND_CODE_CLI_VERSION } from "../src/commandcode-catalog.ts"
|
||||
import {
|
||||
apiForModelId,
|
||||
baseUrlForModel,
|
||||
@@ -100,19 +101,27 @@ describe("commandCodeModelsFromApiResponse()", () => {
|
||||
)
|
||||
})
|
||||
|
||||
it("matches command-code@1.32.1 image input capabilities", () => {
|
||||
it(`uses the command-code@${COMMAND_CODE_CLI_VERSION} image capability catalog`, () => {
|
||||
assert.deepEqual(inputModalitiesForModel("gpt-5.6-luna"), ["text", "image"])
|
||||
assert.deepEqual(inputModalitiesForModel("meta/muse-spark-1.2"), ["text", "image"])
|
||||
assert.deepEqual(inputModalitiesForModel("deepseek/deepseek-v4-flash-vision-exp"), [
|
||||
"text",
|
||||
"image",
|
||||
])
|
||||
assert.deepEqual(inputModalitiesForModel("Qwen/Qwen3.8-27B"), ["text", "image"])
|
||||
assert.deepEqual(inputModalitiesForModel("google/gemini-3.7-flash"), ["text", "image"])
|
||||
assert.deepEqual(inputModalitiesForModel("stealth/ox-alpha"), ["text", "image"])
|
||||
assert.deepEqual(inputModalitiesForModel("deepseek/deepseek-v4-pro"), ["text"])
|
||||
assert.deepEqual(inputModalitiesForModel("zai-org/GLM-5.3"), ["text"])
|
||||
assert.deepEqual(inputModalitiesForModel("unknown-new-model"), ["text"])
|
||||
assert.equal(modelSupportsImageInput("gpt-5.6-luna"), true)
|
||||
assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-flash-vision-exp"), true)
|
||||
assert.equal(modelSupportsImageInput("stealth/ox-alpha"), true)
|
||||
assert.equal(modelSupportsImageInput("deepseek/deepseek-v4-pro"), false)
|
||||
assert.equal(Object.keys(MODEL_INPUT_MODALITIES).length, 38)
|
||||
assert.ok(Object.keys(MODEL_INPUT_MODALITIES).length > 0)
|
||||
for (const modalities of Object.values(MODEL_INPUT_MODALITIES)) {
|
||||
assert.deepEqual(modalities, ["text", "image"])
|
||||
}
|
||||
})
|
||||
|
||||
it("marks only known reasoning models as reasoning-capable", () => {
|
||||
@@ -128,33 +137,14 @@ describe("commandCodeModelsFromApiResponse()", () => {
|
||||
assert.equal(models[1]?.reasoning, false)
|
||||
})
|
||||
|
||||
it("matches the exact command-code@1.32.1 reasoning effort catalog", () => {
|
||||
assert.deepEqual(MODEL_EFFORTS, {
|
||||
"Qwen/Qwen3.8-Max": ["low", "medium", "xhigh"],
|
||||
"claude-fable-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-7": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-4-8": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-opus-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-4-6": ["low", "medium", "high", "xhigh", "max"],
|
||||
"claude-sonnet-5": ["low", "medium", "high", "xhigh", "max"],
|
||||
"deepseek/deepseek-v4-flash": ["high", "max"],
|
||||
"deepseek/deepseek-v4-pro": ["high", "max"],
|
||||
"gpt-5.3-codex": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.4-mini": ["low", "medium", "high"],
|
||||
"gpt-5.5": ["low", "medium", "high", "xhigh"],
|
||||
"gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max"],
|
||||
"gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
|
||||
"google/gemini-3.1-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash": ["low", "medium", "high"],
|
||||
"google/gemini-3.5-flash-lite": ["low", "medium", "high"],
|
||||
"google/gemini-3.6-flash": ["low", "medium", "high"],
|
||||
"sakana/fugu-ultra": ["high", "xhigh"],
|
||||
"xai/grok-4.5": ["low", "medium", "high"],
|
||||
"zai-org/GLM-5.3": ["low", "high", "max"],
|
||||
"zai-org/GLM-5.2": ["high", "max"],
|
||||
})
|
||||
it(`uses the command-code@${COMMAND_CODE_CLI_VERSION} reasoning effort catalog`, () => {
|
||||
const validEfforts = new Set(["minimal", "low", "medium", "high", "xhigh", "max"])
|
||||
assert.ok(Object.keys(MODEL_EFFORTS).length > 0)
|
||||
for (const efforts of Object.values(MODEL_EFFORTS)) {
|
||||
assert.ok(efforts.length > 0)
|
||||
assert.equal(new Set(efforts).size, efforts.length)
|
||||
assert.ok(efforts.every((effort) => validEfforts.has(effort)))
|
||||
}
|
||||
})
|
||||
|
||||
it("builds separate canonical pi and OMP metadata", () => {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import assert from "node:assert/strict"
|
||||
import { after, before, beforeEach, describe, it } from "node:test"
|
||||
|
||||
import { COMMAND_CODE_CLI_VERSION } from "../src/commandcode-catalog.ts"
|
||||
import type { AssistantMessageEvent } from "../src/core.ts"
|
||||
import { MODEL_EFFORTS, thinkingLevelMapForEfforts } from "../src/models.ts"
|
||||
import {
|
||||
@@ -569,7 +570,7 @@ describe("streamCommandCode — request serialization", () => {
|
||||
|
||||
const headers = server.lastRequestHeaders()
|
||||
assert.equal(headers.authorization, "Bearer mock-key")
|
||||
assert.equal(headers["x-command-code-version"], "1.32.1")
|
||||
assert.equal(headers["x-command-code-version"], COMMAND_CODE_CLI_VERSION)
|
||||
assert.equal(headers["x-project-slug"], "repo")
|
||||
assert.equal(headers["x-taste-learning"], "true")
|
||||
assert.equal(headers["x-co-flag"], "false")
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@
|
||||
"strict": true,
|
||||
"types": ["node"]
|
||||
},
|
||||
"include": ["src/**/*.ts", "tests/**/*.ts"]
|
||||
"include": [".github/scripts/**/*.ts", "src/**/*.ts", "tests/**/*.ts"]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user