feat: extract models from command-code dist, load via models.json
- Add scripts/extract-models.ts to parse command-code npm dist file - Generate models.json (21 models, 15 pricing entries) with contextWindow and maxOutputTokens pre-filled; no nulls or hardcoded fallbacks in index.ts - Rewrite index.ts to load model list and costs from models.json - Cap gateway model maxOutputTokens at 65536 (API limit for Baseten/Vercel) - Add 'Update models' section to README documenting the generation flow - Add npm run extract-models script
This commit is contained in:
@@ -100,6 +100,29 @@ Or within pi:
|
|||||||
/models
|
/models
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Update models
|
||||||
|
|
||||||
|
The model list (`models.json`) is extracted from the [command-code](https://www.npmjs.com/package/command-code) npm package's dist file. When Command Code releases a new version with updated models, regenerate it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npm run extract-models
|
||||||
|
```
|
||||||
|
|
||||||
|
This runs `scripts/extract-models.ts`, which:
|
||||||
|
|
||||||
|
1. Downloads the latest `command-code` tarball from npm (`npm pack command-code`)
|
||||||
|
2. Parses the minified `dist/index.mjs` to extract provider definitions, model metadata, and pricing
|
||||||
|
3. Fills in `contextWindow` and `maxOutputTokens` with sensible defaults where the CLI omits them
|
||||||
|
4. Writes the result to `models.json`
|
||||||
|
|
||||||
|
To use a specific version or local dist file:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx tsx scripts/extract-models.ts /path/to/command-code/dist/index.mjs
|
||||||
|
```
|
||||||
|
|
||||||
|
`models.json` is committed to the repo and included in the npm package.
|
||||||
|
|
||||||
## Publish
|
## Publish
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|||||||
@@ -9,158 +9,100 @@
|
|||||||
* 3. Place API key in `~/.commandcode/auth.json` or `~/.pi/agent/auth.json`
|
* 3. Place API key in `~/.commandcode/auth.json` or `~/.pi/agent/auth.json`
|
||||||
* as {"apiKey": "user_..."} or {"commandcode": "user_..."}
|
* as {"apiKey": "user_..."} or {"commandcode": "user_..."}
|
||||||
*
|
*
|
||||||
* Models: deepseek-v4-pro, deepseek-v4-flash, claude-sonnet-4-6, claude-opus-4-7, etc.
|
* Models are sourced from models.json, which is extracted from the command-code
|
||||||
|
* npm package dist file. Run `npx tsx scripts/extract-models.ts` to refresh.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { calculateCost, createAssistantMessageEventStream } from "@mariozechner/pi-ai"
|
import { readFileSync } from "node:fs";
|
||||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent"
|
|
||||||
|
|
||||||
import { createStreamCommandCode, DEFAULT_API_BASE } from "./src/core.ts"
|
import { calculateCost, createAssistantMessageEventStream } from "@mariozechner/pi-ai";
|
||||||
import { getApiKey, login, refreshToken } from "./src/oauth.ts"
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||||
|
|
||||||
const API_BASE = process.env.COMMANDCODE_API_BASE ?? DEFAULT_API_BASE
|
import { createStreamCommandCode, DEFAULT_API_BASE } from "./src/core.ts";
|
||||||
|
import { getApiKey, login, refreshToken } from "./src/oauth.ts";
|
||||||
|
|
||||||
|
const API_BASE = process.env.COMMANDCODE_API_BASE ?? DEFAULT_API_BASE;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Model definitions
|
// Load model definitions from models.json
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
const MODELS = [
|
interface ModelsJson {
|
||||||
// Premium (Anthropic)
|
providers: Record<string, string>;
|
||||||
{
|
models: Array<{
|
||||||
id: "claude-opus-4-7",
|
key: string;
|
||||||
name: "Claude Opus 4.7 (CC)",
|
id: string;
|
||||||
reasoning: true,
|
provider: string;
|
||||||
contextWindow: 200_000,
|
spec: string;
|
||||||
maxTokens: 32_000,
|
label: string;
|
||||||
},
|
name: string;
|
||||||
{
|
description: string;
|
||||||
id: "claude-opus-4-6",
|
reasoning: boolean;
|
||||||
name: "Claude Opus 4.6 (CC)",
|
reasoningEfforts: string[] | null;
|
||||||
reasoning: true,
|
contextWindow: number;
|
||||||
contextWindow: 200_000,
|
maxOutputTokens: number;
|
||||||
maxTokens: 32_000,
|
vendorLabel: string | null;
|
||||||
},
|
}>;
|
||||||
{
|
pricing: Array<{
|
||||||
id: "claude-sonnet-4-6",
|
provider: string;
|
||||||
name: "Claude Sonnet 4.6 (CC)",
|
id: string;
|
||||||
reasoning: true,
|
category: string;
|
||||||
contextWindow: 200_000,
|
promptCost: number;
|
||||||
maxTokens: 16_384,
|
completionCost: number;
|
||||||
},
|
cacheWrite5mCost: number;
|
||||||
{
|
cacheWrite1hCost: number;
|
||||||
id: "claude-haiku-4-5-20251001",
|
cacheHitCost: number;
|
||||||
name: "Claude Haiku 4.5 (CC)",
|
}>;
|
||||||
reasoning: true,
|
}
|
||||||
contextWindow: 200_000,
|
|
||||||
maxTokens: 8_192,
|
const modelsJson: ModelsJson = JSON.parse(
|
||||||
},
|
readFileSync(new URL("./models.json", import.meta.url), "utf8"),
|
||||||
// Premium (OpenAI)
|
);
|
||||||
{
|
|
||||||
id: "gpt-5.5",
|
// ---------------------------------------------------------------------------
|
||||||
name: "GPT-5.5 (CC)",
|
// Build cost lookup (model id -> pricing)
|
||||||
reasoning: true,
|
// ---------------------------------------------------------------------------
|
||||||
contextWindow: 256_000,
|
|
||||||
maxTokens: 128_000,
|
const costByModelId = new Map<string, ModelsJson["pricing"][number]>();
|
||||||
},
|
for (const p of modelsJson.pricing) {
|
||||||
{
|
// Pricing id is like "anthropic:claude-sonnet-4-6"
|
||||||
id: "gpt-5.4",
|
const colonIdx = p.id.indexOf(":");
|
||||||
name: "GPT-5.4 (CC)",
|
if (colonIdx > 0) {
|
||||||
reasoning: true,
|
costByModelId.set(p.id.substring(colonIdx + 1), p);
|
||||||
contextWindow: 256_000,
|
}
|
||||||
maxTokens: 128_000,
|
costByModelId.set(p.id, p);
|
||||||
},
|
}
|
||||||
{
|
|
||||||
id: "gpt-5.3-codex",
|
// ---------------------------------------------------------------------------
|
||||||
name: "GPT-5.3 Codex (CC)",
|
// Build pi model list (all defaults come from models.json)
|
||||||
reasoning: true,
|
// ---------------------------------------------------------------------------
|
||||||
contextWindow: 256_000,
|
|
||||||
maxTokens: 128_000,
|
const MODELS = modelsJson.models.map((m) => {
|
||||||
},
|
const cost = costByModelId.get(m.id);
|
||||||
{
|
return {
|
||||||
id: "gpt-5.4-mini",
|
id: m.id,
|
||||||
name: "GPT-5.4 Mini (CC)",
|
name: `${m.name} (CC)`,
|
||||||
reasoning: false,
|
reasoning: m.reasoning,
|
||||||
contextWindow: 256_000,
|
contextWindow: m.contextWindow,
|
||||||
maxTokens: 128_000,
|
maxTokens: m.maxOutputTokens,
|
||||||
},
|
cost: {
|
||||||
// Open-source
|
input: cost?.promptCost ?? 0,
|
||||||
{
|
output: cost?.completionCost ?? 0,
|
||||||
id: "deepseek/deepseek-v4-pro",
|
cacheRead: cost?.cacheHitCost ?? 0,
|
||||||
name: "DeepSeek V4 Pro (CC)",
|
cacheWrite: Math.max(cost?.cacheWrite5mCost ?? 0, cost?.cacheWrite1hCost ?? 0),
|
||||||
reasoning: true,
|
},
|
||||||
contextWindow: 1_000_000,
|
};
|
||||||
maxTokens: 384_000,
|
});
|
||||||
},
|
|
||||||
{
|
// ---------------------------------------------------------------------------
|
||||||
id: "deepseek/deepseek-v4-flash",
|
// Stream factory
|
||||||
name: "DeepSeek V4 Flash (CC)",
|
// ---------------------------------------------------------------------------
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 1_000_000,
|
|
||||||
maxTokens: 384_000,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "moonshotai/Kimi-K2.6",
|
|
||||||
name: "Kimi K2.6 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 262_144,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "moonshotai/Kimi-K2.5",
|
|
||||||
name: "Kimi K2.5 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 262_144,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "zai-org/GLM-5.1",
|
|
||||||
name: "GLM-5.1 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 200_000,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "zai-org/GLM-5",
|
|
||||||
name: "GLM-5 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 200_000,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "MiniMaxAI/MiniMax-M2.7",
|
|
||||||
name: "MiniMax M2.7 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 1_048_576,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "MiniMaxAI/MiniMax-M2.5",
|
|
||||||
name: "MiniMax M2.5 (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 1_048_576,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "Qwen/Qwen3.6-Max-Preview",
|
|
||||||
name: "Qwen 3.6 Max (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 1_000_000,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "Qwen/Qwen3.6-Plus",
|
|
||||||
name: "Qwen 3.6 Plus (CC)",
|
|
||||||
reasoning: true,
|
|
||||||
contextWindow: 1_000_000,
|
|
||||||
maxTokens: 131_072,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const streamCommandCode = createStreamCommandCode({
|
const streamCommandCode = createStreamCommandCode({
|
||||||
createStream: createAssistantMessageEventStream,
|
createStream: createAssistantMessageEventStream,
|
||||||
calculateCost,
|
calculateCost,
|
||||||
apiBase: API_BASE,
|
apiBase: API_BASE,
|
||||||
})
|
});
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Extension entry point
|
// Extension entry point
|
||||||
@@ -188,10 +130,10 @@ export default function (pi: ExtensionAPI) {
|
|||||||
id: model.id,
|
id: model.id,
|
||||||
name: model.name,
|
name: model.name,
|
||||||
reasoning: model.reasoning,
|
reasoning: model.reasoning,
|
||||||
input: ["text"],
|
input: ["text"] as const,
|
||||||
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
cost: model.cost,
|
||||||
contextWindow: model.contextWindow,
|
contextWindow: model.contextWindow,
|
||||||
maxTokens: model.maxTokens,
|
maxTokens: model.maxTokens,
|
||||||
})),
|
})),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+545
@@ -0,0 +1,545 @@
|
|||||||
|
{
|
||||||
|
"providers": {
|
||||||
|
"ANTHROPIC": "anthropic",
|
||||||
|
"OPENAI": "openai",
|
||||||
|
"BASETEN": "baseten",
|
||||||
|
"VERCEL_AI_GATEWAY": "vercel-ai-gateway",
|
||||||
|
"CLOUDFLARE_AI_GATEWAY": "cloudflare-ai-gateway",
|
||||||
|
"OPENROUTER": "openrouter"
|
||||||
|
},
|
||||||
|
"providerGroups": [
|
||||||
|
{
|
||||||
|
"id": "command-code",
|
||||||
|
"label": "Command Code",
|
||||||
|
"shortLabel": "cmd",
|
||||||
|
"description": "recommended",
|
||||||
|
"providers": [
|
||||||
|
"anthropic",
|
||||||
|
"openai",
|
||||||
|
"baseten",
|
||||||
|
"vercel-ai-gateway"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "anthropic",
|
||||||
|
"label": "Anthropic",
|
||||||
|
"shortLabel": "anth",
|
||||||
|
"description": "Claude Pro/Max",
|
||||||
|
"providers": [
|
||||||
|
"anthropic"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "github-copilot",
|
||||||
|
"label": "GitHub Copilot",
|
||||||
|
"shortLabel": "copilot",
|
||||||
|
"description": "Copilot subscription",
|
||||||
|
"providers": [
|
||||||
|
"anthropic",
|
||||||
|
"openai"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "codex",
|
||||||
|
"label": "ChatGPT (Codex)",
|
||||||
|
"shortLabel": "codex",
|
||||||
|
"description": "ChatGPT Pro/Plus subscription",
|
||||||
|
"providers": [
|
||||||
|
"openai"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"models": [
|
||||||
|
{
|
||||||
|
"key": "SONNET_4_6",
|
||||||
|
"id": "claude-sonnet-4-6",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Claude Sonnet 4.6",
|
||||||
|
"name": "Claude Sonnet 4.6",
|
||||||
|
"description": "best combo of speed & intelligence (recommended)",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"xhigh",
|
||||||
|
"max"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 64000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "OPUS_4_7",
|
||||||
|
"id": "claude-opus-4-7",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Claude Opus 4.7",
|
||||||
|
"name": "Claude Opus 4.7",
|
||||||
|
"description": "most intelligent for agents and coding",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"xhigh",
|
||||||
|
"max"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 64000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "HAIKU_4_5",
|
||||||
|
"id": "claude-haiku-4-5-20251001",
|
||||||
|
"provider": "anthropic",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Claude Haiku 4.5",
|
||||||
|
"name": "Claude Haiku 4.5",
|
||||||
|
"description": "fastest & most compact, great for quick tasks",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 200000,
|
||||||
|
"maxOutputTokens": 64000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GPT_5_5",
|
||||||
|
"id": "gpt-5.5",
|
||||||
|
"provider": "openai",
|
||||||
|
"spec": "responses",
|
||||||
|
"label": "GPT-5.5",
|
||||||
|
"name": "GPT-5.5",
|
||||||
|
"description": "latest frontier model for general complex work",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"xhigh"
|
||||||
|
],
|
||||||
|
"contextWindow": 256000,
|
||||||
|
"maxOutputTokens": 128000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GPT_5_4",
|
||||||
|
"id": "gpt-5.4",
|
||||||
|
"provider": "openai",
|
||||||
|
"spec": "responses",
|
||||||
|
"label": "GPT-5.4",
|
||||||
|
"name": "GPT-5.4",
|
||||||
|
"description": "frontier model for general complex work",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"xhigh"
|
||||||
|
],
|
||||||
|
"contextWindow": 400000,
|
||||||
|
"maxOutputTokens": 128000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GPT_5_3_CODEX",
|
||||||
|
"id": "gpt-5.3-codex",
|
||||||
|
"provider": "openai",
|
||||||
|
"spec": "responses",
|
||||||
|
"label": "GPT-5.3 Codex",
|
||||||
|
"name": "GPT-5.3 Codex",
|
||||||
|
"description": "frontier coding model",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high",
|
||||||
|
"xhigh"
|
||||||
|
],
|
||||||
|
"contextWindow": 400000,
|
||||||
|
"maxOutputTokens": 128000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GPT_5_4_MINI",
|
||||||
|
"id": "gpt-5.4-mini",
|
||||||
|
"provider": "openai",
|
||||||
|
"spec": "responses",
|
||||||
|
"label": "GPT-5.4 Mini",
|
||||||
|
"name": "GPT-5.4 Mini",
|
||||||
|
"description": "fast, cost-effective model for everyday tasks",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high"
|
||||||
|
],
|
||||||
|
"contextWindow": 400000,
|
||||||
|
"maxOutputTokens": 128000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "KIMI_K2_6",
|
||||||
|
"id": "moonshotai/Kimi-K2.6",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Kimi K2.6",
|
||||||
|
"name": "Kimi K2.6",
|
||||||
|
"description": "long-horizon coding with vision",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 256000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "KIMI_K2_5",
|
||||||
|
"id": "moonshotai/Kimi-K2.5",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Kimi K2.5",
|
||||||
|
"name": "Kimi K2.5",
|
||||||
|
"description": "multimodal frontend coding",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 256000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GLM_5_1",
|
||||||
|
"id": "zai-org/GLM-5.1",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "GLM-5.1",
|
||||||
|
"name": "GLM-5.1",
|
||||||
|
"description": "long-horizon autonomous coding agent",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 200000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GLM_5",
|
||||||
|
"id": "zai-org/GLM-5",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "GLM-5",
|
||||||
|
"name": "GLM-5",
|
||||||
|
"description": "multi-mode thinking & long-range planning",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 200000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MINIMAX_M2_7",
|
||||||
|
"id": "MiniMaxAI/MiniMax-M2.7",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "MiniMax M2.7",
|
||||||
|
"name": "MiniMax M2.7",
|
||||||
|
"description": "end-to-end software engineering agent",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 1048576,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "MINIMAX_M2_5",
|
||||||
|
"id": "MiniMaxAI/MiniMax-M2.5",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "MiniMax M2.5",
|
||||||
|
"name": "MiniMax M2.5",
|
||||||
|
"description": "cross-platform full-stack agentic dev",
|
||||||
|
"reasoning": false,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 200000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "DEEPSEEK_V4_PRO",
|
||||||
|
"id": "deepseek/deepseek-v4-pro",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "DeepSeek V4 Pro",
|
||||||
|
"name": "DeepSeek V4 Pro",
|
||||||
|
"description": "hybrid-attention long-context reasoning",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"high",
|
||||||
|
"max"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 384000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "DEEPSEEK_V4_FLASH",
|
||||||
|
"id": "deepseek/deepseek-v4-flash",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "DeepSeek V4 Flash",
|
||||||
|
"name": "DeepSeek V4 Flash",
|
||||||
|
"description": "fast hybrid-attention reasoning",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"high",
|
||||||
|
"max"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 384000,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "QWEN_3_6_MAX_PREVIEW",
|
||||||
|
"id": "Qwen/Qwen3.6-Max-Preview",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Qwen 3.6 Max Preview",
|
||||||
|
"name": "Qwen 3.6 Max Preview",
|
||||||
|
"description": "vibe coding & efficient agent execution",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "QWEN_3_6_PLUS",
|
||||||
|
"id": "Qwen/Qwen3.6-Plus",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Qwen 3.6 Plus",
|
||||||
|
"name": "Qwen 3.6 Plus",
|
||||||
|
"description": "agentic coding & reasoning",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "QWEN_3_7_MAX",
|
||||||
|
"id": "Qwen/Qwen3.7-Max",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Qwen 3.7 Max",
|
||||||
|
"name": "Qwen 3.7 Max",
|
||||||
|
"description": "frontier coding & long-horizon agent execution",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "STEP_3_5_FLASH",
|
||||||
|
"id": "stepfun/Step-3.5-Flash",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Step 3.5 Flash",
|
||||||
|
"name": "Step 3.5 Flash",
|
||||||
|
"description": "fast sparse-MoE agentic reasoning",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": null,
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GEMINI_3_5_FLASH",
|
||||||
|
"id": "google/gemini-3.5-flash",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Gemini 3.5 Flash",
|
||||||
|
"name": "Gemini 3.5 Flash",
|
||||||
|
"description": "Pro-level coding proficiency, parallel agentic execution",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": "Google"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "GEMINI_3_1_FLASH_LITE",
|
||||||
|
"id": "google/gemini-3.1-flash-lite",
|
||||||
|
"provider": "vercel-ai-gateway",
|
||||||
|
"spec": "chatComplete",
|
||||||
|
"label": "Gemini 3.1 Flash Lite",
|
||||||
|
"name": "Gemini 3.1 Flash Lite",
|
||||||
|
"description": "high-volume workhorse model with implicit caching",
|
||||||
|
"reasoning": true,
|
||||||
|
"reasoningEfforts": [
|
||||||
|
"low",
|
||||||
|
"medium",
|
||||||
|
"high"
|
||||||
|
],
|
||||||
|
"contextWindow": 1000000,
|
||||||
|
"maxOutputTokens": 65536,
|
||||||
|
"vendorLabel": "Google"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"pricing": [
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-sonnet-4-20250514",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 3,
|
||||||
|
"completionCost": 15,
|
||||||
|
"cacheWrite5mCost": 3.75,
|
||||||
|
"cacheWrite1hCost": 6,
|
||||||
|
"cacheHitCost": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-sonnet-4-5-20250929",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 3,
|
||||||
|
"completionCost": 15,
|
||||||
|
"cacheWrite5mCost": 3.75,
|
||||||
|
"cacheWrite1hCost": 6,
|
||||||
|
"cacheHitCost": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-opus-4-5-20251101",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 5,
|
||||||
|
"completionCost": 25,
|
||||||
|
"cacheWrite5mCost": 6.25,
|
||||||
|
"cacheWrite1hCost": 10,
|
||||||
|
"cacheHitCost": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-sonnet-4-6",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 3,
|
||||||
|
"completionCost": 15,
|
||||||
|
"cacheWrite5mCost": 3.75,
|
||||||
|
"cacheWrite1hCost": 6,
|
||||||
|
"cacheHitCost": 0.3
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-opus-4-7",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 5,
|
||||||
|
"completionCost": 25,
|
||||||
|
"cacheWrite5mCost": 6.25,
|
||||||
|
"cacheWrite1hCost": 10,
|
||||||
|
"cacheHitCost": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-opus-4-6",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 5,
|
||||||
|
"completionCost": 25,
|
||||||
|
"cacheWrite5mCost": 6.25,
|
||||||
|
"cacheWrite1hCost": 10,
|
||||||
|
"cacheHitCost": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Anthropic",
|
||||||
|
"id": "anthropic:claude-haiku-4-5-20251001",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 1,
|
||||||
|
"completionCost": 5,
|
||||||
|
"cacheWrite5mCost": 1.25,
|
||||||
|
"cacheWrite1hCost": 2,
|
||||||
|
"cacheHitCost": 0.1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"id": "openai:gpt-5.5",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 5,
|
||||||
|
"completionCost": 30,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"id": "openai:gpt-5.4",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 2.5,
|
||||||
|
"completionCost": 15,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0.25
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"id": "openai:gpt-5.3-codex",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 2,
|
||||||
|
"completionCost": 8,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0.5
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "OpenAI",
|
||||||
|
"id": "openai:gpt-5.4-mini",
|
||||||
|
"category": "premium",
|
||||||
|
"promptCost": 0.75,
|
||||||
|
"completionCost": 4.5,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0.075
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Baseten",
|
||||||
|
"id": "baseten:zai-org/GLM-5",
|
||||||
|
"category": "opensource",
|
||||||
|
"promptCost": 0.95,
|
||||||
|
"completionCost": 3.15,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Baseten",
|
||||||
|
"id": "baseten:moonshotai/Kimi-K2.5",
|
||||||
|
"category": "opensource",
|
||||||
|
"promptCost": 0.6,
|
||||||
|
"completionCost": 3,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Baseten",
|
||||||
|
"id": "baseten:moonshotai/Kimi-K2.6",
|
||||||
|
"category": "opensource",
|
||||||
|
"promptCost": 0.95,
|
||||||
|
"completionCost": 4,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0.16
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"provider": "Baseten",
|
||||||
|
"id": "baseten:MiniMaxAI/MiniMax-M2.5",
|
||||||
|
"category": "opensource",
|
||||||
|
"promptCost": 0.5,
|
||||||
|
"completionCost": 2,
|
||||||
|
"cacheWrite5mCost": 0,
|
||||||
|
"cacheWrite1hCost": 0,
|
||||||
|
"cacheHitCost": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+3
-1
@@ -21,6 +21,7 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"index.ts",
|
"index.ts",
|
||||||
"src/",
|
"src/",
|
||||||
|
"models.json",
|
||||||
"README.md",
|
"README.md",
|
||||||
"LICENSE"
|
"LICENSE"
|
||||||
],
|
],
|
||||||
@@ -34,7 +35,8 @@
|
|||||||
"test:abort": "tsx tests/test-abort.ts",
|
"test:abort": "tsx tests/test-abort.ts",
|
||||||
"test:stream": "tsx tests/test-stream.ts",
|
"test:stream": "tsx tests/test-stream.ts",
|
||||||
"test:pi-local": "node tests/test-pi-local.mjs",
|
"test:pi-local": "node tests/test-pi-local.mjs",
|
||||||
"test:smoke": "node tests/test-smoke.mjs"
|
"test:smoke": "node tests/test-smoke.mjs",
|
||||||
|
"extract-models": "tsx scripts/extract-models.ts"
|
||||||
},
|
},
|
||||||
"pi": {
|
"pi": {
|
||||||
"extensions": [
|
"extensions": [
|
||||||
|
|||||||
@@ -0,0 +1,252 @@
|
|||||||
|
#!/usr/bin/env -S npx tsx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extract model & provider definitions from the command-code npm package dist file.
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* npx tsx scripts/extract-models.ts [path-to-dist/index.mjs]
|
||||||
|
* npx tsx scripts/extract-models.ts (downloads latest from npm)
|
||||||
|
*
|
||||||
|
* Output: models.json
|
||||||
|
* {
|
||||||
|
* providers: { ... }, // provider key -> value map
|
||||||
|
* providerGroups: { ... }, // provider-group key -> { id, label, providers[] }
|
||||||
|
* models: [ ... ], // flattened model definitions
|
||||||
|
* pricing: [ ... ] // pricing entries (per 1M tokens, USD)
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { execSync } from "node:child_process";
|
||||||
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||||
|
import { join } from "node:path";
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Helpers
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/** Evaluate a JS object literal string safely via Function constructor. */
|
||||||
|
function parseObjectLiteral(code: string): Record<string, unknown> {
|
||||||
|
const fn = new Function(`return (${code})`);
|
||||||
|
return fn() as Record<string, unknown>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Step 1: get the dist file
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function ensureDist(srcPath?: string): string {
|
||||||
|
if (srcPath) {
|
||||||
|
if (!existsSync(srcPath)) throw new Error(`File not found: ${srcPath}`);
|
||||||
|
return srcPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download latest from npm
|
||||||
|
const tmpDir = join(process.cwd(), ".extract-tmp");
|
||||||
|
mkdirSync(tmpDir, { recursive: true });
|
||||||
|
const tgz = execSync(`npm pack command-code --pack-destination "${tmpDir}"`, {
|
||||||
|
encoding: "utf8",
|
||||||
|
}).trim();
|
||||||
|
const tgzPath = join(tmpDir, tgz);
|
||||||
|
execSync(`tar xzf "${tgzPath}" -C "${tmpDir}"`, { encoding: "utf8" });
|
||||||
|
return join(tmpDir, "package", "dist", "index.mjs");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Step 2: extract
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
interface ModelDef {
|
||||||
|
key: string;
|
||||||
|
id: string;
|
||||||
|
provider: string;
|
||||||
|
spec: string;
|
||||||
|
label: string;
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
reasoning: boolean;
|
||||||
|
reasoningEfforts: string[] | null;
|
||||||
|
contextWindow: number;
|
||||||
|
maxOutputTokens: number;
|
||||||
|
vendorLabel: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PricingEntry {
|
||||||
|
provider: string;
|
||||||
|
id: string;
|
||||||
|
category: string;
|
||||||
|
promptCost: number;
|
||||||
|
completionCost: number;
|
||||||
|
cacheWrite5mCost: number;
|
||||||
|
cacheWrite1hCost: number;
|
||||||
|
cacheHitCost: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProviderGroup {
|
||||||
|
id: string;
|
||||||
|
label: string;
|
||||||
|
shortLabel: string;
|
||||||
|
description: string;
|
||||||
|
providers: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function extract(code: string) {
|
||||||
|
// --- Wt: provider constants ---
|
||||||
|
const wtMatch = code.match(/Wt=\{([^}]+)\}/);
|
||||||
|
if (!wtMatch) throw new Error("Cannot find Wt");
|
||||||
|
const wtRaw = "{" + wtMatch[1] + "}";
|
||||||
|
const wt: Record<string, string> = {};
|
||||||
|
for (const m of wtRaw.matchAll(/(\w+):"(\w[-\w]*)"/g)) {
|
||||||
|
wt[m[1]] = m[2];
|
||||||
|
}
|
||||||
|
console.log("Providers:", wt);
|
||||||
|
|
||||||
|
// --- an: model definitions ---
|
||||||
|
// an={...}).SONNET
|
||||||
|
const anIdx = code.indexOf("an={");
|
||||||
|
if (anIdx < 0) throw new Error("Cannot find an");
|
||||||
|
const anEndIdx = code.indexOf("}).SONNET", anIdx);
|
||||||
|
if (anEndIdx < 0) throw new Error("Cannot find end of an");
|
||||||
|
let anCode = code.substring(anIdx + 1, anEndIdx + 2); // "an={...})"
|
||||||
|
anCode = anCode.replace(/^\(an=/, "").replace(/\)$/, "");
|
||||||
|
|
||||||
|
// Replace minified JS idioms
|
||||||
|
anCode = anCode.replace(/\bQt\b/g, JSON.stringify("vercel-ai-gateway"));
|
||||||
|
anCode = anCode.replace(/\bon\b/g, JSON.stringify("chatComplete"));
|
||||||
|
anCode = anCode.replace(/\bsn\b/g, JSON.stringify("responses"));
|
||||||
|
anCode = anCode.replace(/Wt\.([A-Z_]+)/g, (_, key: string) =>
|
||||||
|
JSON.stringify(wt[key]),
|
||||||
|
);
|
||||||
|
anCode = anCode.replace(/!0/g, "true");
|
||||||
|
anCode = anCode.replace(/!1/g, "false");
|
||||||
|
|
||||||
|
const an = parseObjectLiteral(anCode);
|
||||||
|
|
||||||
|
// --- Yt: pricing (provider -> model array) ---
|
||||||
|
const ytIdx = code.indexOf("Yt={[");
|
||||||
|
if (ytIdx < 0) throw new Error("Cannot find Yt");
|
||||||
|
// Find the matching closing brace for Yt
|
||||||
|
let depth = 1;
|
||||||
|
let ytEndIdx = ytIdx + 4;
|
||||||
|
while (depth > 0 && ytEndIdx < code.length) {
|
||||||
|
if (code[ytEndIdx] === "{") depth++;
|
||||||
|
else if (code[ytEndIdx] === "}") depth--;
|
||||||
|
ytEndIdx++;
|
||||||
|
}
|
||||||
|
let ytCode = code.substring(ytIdx + 3, ytEndIdx); // "{ ... }"
|
||||||
|
ytCode = ytCode.replace(/Wt\.([A-Z_]+)/g, (_, key: string) =>
|
||||||
|
JSON.stringify(wt[key]),
|
||||||
|
);
|
||||||
|
ytCode = ytCode.replace(/!0/g, "true");
|
||||||
|
ytCode = ytCode.replace(/!1/g, "false");
|
||||||
|
const yt = parseObjectLiteral(ytCode);
|
||||||
|
|
||||||
|
// --- pn: provider groups ---
|
||||||
|
const pnIdx = code.indexOf('pn={"command-code"');
|
||||||
|
if (pnIdx < 0) throw new Error("Cannot find pn");
|
||||||
|
const pnEndIdx = code.indexOf(",__name(buildModelGroups", pnIdx);
|
||||||
|
if (pnEndIdx < 0) throw new Error("Cannot find end of pn");
|
||||||
|
let pnCode = code.substring(pnIdx + 3, pnEndIdx);
|
||||||
|
pnCode = pnCode.replace(/Wt\.([A-Z_]+)/g, (_, key: string) =>
|
||||||
|
JSON.stringify(wt[key]),
|
||||||
|
);
|
||||||
|
pnCode = pnCode.replace(/!0/g, "true");
|
||||||
|
pnCode = pnCode.replace(/!1/g, "false");
|
||||||
|
pnCode = pnCode.replace(/,\s*$/, "");
|
||||||
|
const pn = parseObjectLiteral(pnCode);
|
||||||
|
|
||||||
|
// --- Defaults for fields the CLI doesn't provide per-model ---
|
||||||
|
// maxOutputTokens: use the minimum across all providers for each model.
|
||||||
|
// Anthropic direct: 64k OpenAI direct: 128k
|
||||||
|
// DeepSeek (gateway): 384k (known to work)
|
||||||
|
// Other gateway models (Baseten/Vercel/Cloudflare/OpenRouter): 65536
|
||||||
|
const CONTEXT_WINDOW_FALLBACKS: Record<string, number> = {
|
||||||
|
"gpt-5.5": 256_000,
|
||||||
|
"zai-org/GLM-5.1": 200_000,
|
||||||
|
"MiniMaxAI/MiniMax-M2.7": 1_048_576,
|
||||||
|
"Qwen/Qwen3.6-Max-Preview": 1_000_000,
|
||||||
|
"Qwen/Qwen3.6-Plus": 1_000_000,
|
||||||
|
};
|
||||||
|
const DEFAULT_CONTEXT_WINDOW = 200_000;
|
||||||
|
|
||||||
|
function maxOutputTokensForModel(id: string, provider: string): number {
|
||||||
|
if (provider === "anthropic") return 64_000;
|
||||||
|
if (provider === "openai") return 128_000;
|
||||||
|
if (id.startsWith("deepseek/")) return 384_000;
|
||||||
|
// Gateway models — lowest common denominator across Baseten/Vercel/Cloudflare
|
||||||
|
return 65_536;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Build output ---
|
||||||
|
const models: ModelDef[] = [];
|
||||||
|
for (const [key, obj] of Object.entries(an)) {
|
||||||
|
const m = obj as Record<string, unknown>;
|
||||||
|
const id = m.id as string;
|
||||||
|
const provider = m.provider as string;
|
||||||
|
models.push({
|
||||||
|
key,
|
||||||
|
id,
|
||||||
|
provider,
|
||||||
|
spec: (m.spec as string) || "chatComplete",
|
||||||
|
label: m.label as string,
|
||||||
|
name: m.name as string,
|
||||||
|
description: m.description as string,
|
||||||
|
reasoning: !!(
|
||||||
|
m.reasoning ??
|
||||||
|
((m.reasoningEfforts as string[])?.length ?? 0) > 0
|
||||||
|
),
|
||||||
|
reasoningEfforts: (m.reasoningEfforts as string[]) || null,
|
||||||
|
contextWindow:
|
||||||
|
(typeof m.contextWindow === "number" ? m.contextWindow : null) ??
|
||||||
|
CONTEXT_WINDOW_FALLBACKS[id] ??
|
||||||
|
DEFAULT_CONTEXT_WINDOW,
|
||||||
|
maxOutputTokens:
|
||||||
|
maxOutputTokensForModel(id, provider),
|
||||||
|
vendorLabel: (m.vendorLabel as string) || null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const pricing: PricingEntry[] = [];
|
||||||
|
for (const [_provider, entries] of Object.entries(yt)) {
|
||||||
|
for (const entry of entries as Array<Record<string, unknown>>) {
|
||||||
|
pricing.push({
|
||||||
|
provider: entry.provider as string,
|
||||||
|
id: entry.id as string,
|
||||||
|
category: entry.category as string,
|
||||||
|
promptCost: entry.promptCost as number,
|
||||||
|
completionCost: entry.completionCost as number,
|
||||||
|
cacheWrite5mCost: (entry.cacheWrite5mCost as number) || 0,
|
||||||
|
cacheWrite1hCost: (entry.cacheWrite1hCost as number) || 0,
|
||||||
|
cacheHitCost: (entry.cacheHitCost as number) || 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerGroups: ProviderGroup[] = [];
|
||||||
|
for (const [key, obj] of Object.entries(pn)) {
|
||||||
|
const g = obj as Record<string, unknown>;
|
||||||
|
providerGroups.push({
|
||||||
|
id: g.id as string,
|
||||||
|
label: g.label as string,
|
||||||
|
shortLabel: (g.shortLabel as string) || "",
|
||||||
|
description: (g.description as string) || "",
|
||||||
|
providers: (g.supportedModelProviders as string[]) || [],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { providers: wt, providerGroups, models, pricing };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// main
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const distPath = ensureDist(process.argv[2]);
|
||||||
|
console.log("Reading:", distPath);
|
||||||
|
const code = readFileSync(distPath, "utf8");
|
||||||
|
const result = extract(code);
|
||||||
|
|
||||||
|
const outPath = join(process.cwd(), "models.json");
|
||||||
|
writeFileSync(outPath, JSON.stringify(result, null, 2), "utf8");
|
||||||
|
console.log(
|
||||||
|
`Wrote ${result.models.length} models + ${result.pricing.length} pricing entries to ${outPath}`,
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user