pi-tokenrouter 1.0.4 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -7
- package/index.ts +7 -288
- package/models.generated.ts +1003 -0
- package/package.json +6 -1
- package/provider-config.ts +30 -0
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A [pi](https://github.com/badlogic/pi-mono) provider extension for [TokenRouter](https://tokenrouter.com).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Models are derived from TokenRouter's `/v1/models` list and enriched with metadata from [models.dev](https://models.dev) first, then [OpenRouter](https://openrouter.ai).
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -33,12 +33,9 @@ export TOKENROUTER_API_KEY=sk-...
|
|
|
33
33
|
|
|
34
34
|
## How it works
|
|
35
35
|
|
|
36
|
-
1.
|
|
37
|
-
2.
|
|
38
|
-
3.
|
|
39
|
-
4. Caches everything locally for 1 week (`~/.pi/agent/cache/tokenrouter-models.json`).
|
|
40
|
-
|
|
41
|
-
Models that don't match an OpenRouter entry fall back to zero cost and default context limits.
|
|
36
|
+
1. Registers TokenRouter as an API-key provider, so `/login tokenrouter` is handled under `Use an API key`.
|
|
37
|
+
2. Uses a checked-in snapshot generated from TokenRouter's authenticated `/v1/models` response.
|
|
38
|
+
3. Enriches each model with metadata from `models.dev` when available, then falls back to OpenRouter for pricing, context window, max output tokens, reasoning support, and image support.
|
|
42
39
|
|
|
43
40
|
## License
|
|
44
41
|
|
package/index.ts
CHANGED
|
@@ -2,304 +2,23 @@
|
|
|
2
2
|
* TokenRouter Provider Extension
|
|
3
3
|
*
|
|
4
4
|
* Registers TokenRouter (https://api.tokenrouter.com/v1) as a custom provider.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* Authentication (resolved via AuthStorage with full priority chain):
|
|
9
|
-
* 1. Runtime overrides (CLI --api-key)
|
|
10
|
-
* 2. API key from auth.json (literal, env var name, or shell command)
|
|
11
|
-
* 3. OAuth token from auth.json (from /login tokenrouter)
|
|
12
|
-
* 4. TOKENROUTER_API_KEY environment variable
|
|
5
|
+
* Models are derived from TokenRouter's /v1/models list, enriched with
|
|
6
|
+
* metadata from models.dev first and OpenRouter second, so TokenRouter can be
|
|
7
|
+
* configured through pi's API-key login flow before any TokenRouter auth exists.
|
|
13
8
|
*
|
|
14
9
|
* Usage:
|
|
15
10
|
* pi -e /path/to/pi-tokenrouter
|
|
16
|
-
* /login tokenrouter
|
|
11
|
+
* /login tokenrouter
|
|
17
12
|
* # OR add to ~/.pi/agent/auth.json:
|
|
18
13
|
* # "tokenrouter": { "type": "api_key", "key": "sk-..." }
|
|
19
14
|
* # "tokenrouter": { "type": "api_key", "key": "TOKENROUTER_API_KEY" }
|
|
20
15
|
* # "tokenrouter": { "type": "api_key", "key": "!op read 'op://vault/item/key'" }
|
|
21
16
|
*/
|
|
22
17
|
|
|
23
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
24
|
-
import { join } from "node:path";
|
|
25
|
-
import { homedir } from "node:os";
|
|
26
|
-
import type { OAuthCredentials, OAuthLoginCallbacks } from "@mariozechner/pi-ai";
|
|
27
|
-
import { AuthStorage } from "@mariozechner/pi-coding-agent";
|
|
28
18
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const PROVIDER_NAME = "tokenrouter";
|
|
32
|
-
const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
33
|
-
|
|
34
|
-
const HOME_PI = join(homedir(), ".pi", "agent");
|
|
35
|
-
const CACHE_DIR = join(HOME_PI, "cache");
|
|
36
|
-
const CACHE_FILE = join(CACHE_DIR, "tokenrouter-models.json");
|
|
37
|
-
const CACHE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
|
|
38
|
-
|
|
39
|
-
const DEFAULT_CONTEXT_WINDOW = 128_000;
|
|
40
|
-
const DEFAULT_MAX_TOKENS = 4_096;
|
|
41
|
-
|
|
42
|
-
// Reasoning model name patterns
|
|
43
|
-
const REASONING_PATTERNS = [
|
|
44
|
-
/o1\b/i,
|
|
45
|
-
/o3\b/i,
|
|
46
|
-
/o4\b/i,
|
|
47
|
-
/claude.*thinking/i,
|
|
48
|
-
/deepseek-r/i,
|
|
49
|
-
/deepseek-prover/i,
|
|
50
|
-
/gemini.*thinking/i,
|
|
51
|
-
/qwq/i,
|
|
52
|
-
/qwen3/i,
|
|
53
|
-
/reasoning/i,
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
// Vision model name patterns
|
|
57
|
-
const VISION_PATTERNS = [
|
|
58
|
-
/vision/i,
|
|
59
|
-
/claude/i,
|
|
60
|
-
/gpt-4o/i,
|
|
61
|
-
/gpt-4-turbo/i,
|
|
62
|
-
/gemini/i,
|
|
63
|
-
/llava/i,
|
|
64
|
-
/qwen.*vl/i,
|
|
65
|
-
/qwen.*visual/i,
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
function isReasoningModel(id: string): boolean {
|
|
69
|
-
return REASONING_PATTERNS.some((p) => p.test(id));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function supportsVision(id: string): boolean {
|
|
73
|
-
return VISION_PATTERNS.some((p) => p.test(id));
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// ---------------------------------------------------------------------------
|
|
77
|
-
// OpenRouter pricing lookup
|
|
78
|
-
// ---------------------------------------------------------------------------
|
|
79
|
-
|
|
80
|
-
interface OpenRouterModel {
|
|
81
|
-
id: string;
|
|
82
|
-
context_length?: number;
|
|
83
|
-
top_provider?: { max_completion_tokens?: number };
|
|
84
|
-
pricing?: {
|
|
85
|
-
prompt?: string;
|
|
86
|
-
completion?: string;
|
|
87
|
-
input_cache_read?: string;
|
|
88
|
-
input_cache_write?: string;
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
interface Pricing {
|
|
93
|
-
input: number;
|
|
94
|
-
output: number;
|
|
95
|
-
cacheRead: number;
|
|
96
|
-
cacheWrite: number;
|
|
97
|
-
contextWindow: number;
|
|
98
|
-
maxTokens: number;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function parsePerToken(price?: string): number {
|
|
102
|
-
const n = parseFloat(price ?? "0");
|
|
103
|
-
return Number.isFinite(n) ? n * 1_000_000 : 0;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async function fetchOpenRouterPricing(): Promise<Map<string, Pricing>> {
|
|
107
|
-
const response = await fetch(OPENROUTER_MODELS_URL);
|
|
108
|
-
if (!response.ok) return new Map();
|
|
109
|
-
|
|
110
|
-
const payload = (await response.json()) as { data?: OpenRouterModel[] };
|
|
111
|
-
const models = payload.data ?? [];
|
|
112
|
-
|
|
113
|
-
const map = new Map<string, Pricing>();
|
|
114
|
-
for (const m of models) {
|
|
115
|
-
map.set(m.id, {
|
|
116
|
-
input: parsePerToken(m.pricing?.prompt),
|
|
117
|
-
output: parsePerToken(m.pricing?.completion),
|
|
118
|
-
cacheRead: parsePerToken(m.pricing?.input_cache_read),
|
|
119
|
-
cacheWrite: parsePerToken(m.pricing?.input_cache_write),
|
|
120
|
-
contextWindow: m.context_length ?? DEFAULT_CONTEXT_WINDOW,
|
|
121
|
-
maxTokens: m.top_provider?.max_completion_tokens ?? DEFAULT_MAX_TOKENS,
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
return map;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// Model cache
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
interface CachedModels {
|
|
132
|
-
fetchedAt: number;
|
|
133
|
-
models: RawModel[];
|
|
134
|
-
pricing: Record<string, Pricing>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
interface RawModel {
|
|
138
|
-
id: string;
|
|
139
|
-
name?: string;
|
|
140
|
-
context_window?: number;
|
|
141
|
-
max_tokens?: number;
|
|
142
|
-
[key: string]: unknown;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function readCache(): CachedModels | null {
|
|
146
|
-
try {
|
|
147
|
-
if (!existsSync(CACHE_FILE)) return null;
|
|
148
|
-
const raw = readFileSync(CACHE_FILE, "utf-8");
|
|
149
|
-
const data = JSON.parse(raw) as CachedModels;
|
|
150
|
-
// Invalidate cache from before pricing was added
|
|
151
|
-
if (!data.pricing) return null;
|
|
152
|
-
return data;
|
|
153
|
-
} catch {
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function writeCache(models: RawModel[], pricing: Map<string, Pricing>): void {
|
|
159
|
-
try {
|
|
160
|
-
if (!existsSync(CACHE_DIR)) {
|
|
161
|
-
mkdirSync(CACHE_DIR, { recursive: true });
|
|
162
|
-
}
|
|
163
|
-
// Serialise pricing map to plain object
|
|
164
|
-
const pricingObj: Record<string, Pricing> = {};
|
|
165
|
-
for (const [k, v] of pricing) pricingObj[k] = v;
|
|
166
|
-
const payload: CachedModels = { fetchedAt: Date.now(), models, pricing: pricingObj };
|
|
167
|
-
writeFileSync(CACHE_FILE, JSON.stringify(payload), "utf-8");
|
|
168
|
-
} catch {
|
|
169
|
-
// Cache write failure is non-fatal
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function isCacheFresh(cache: CachedModels): boolean {
|
|
174
|
-
return Date.now() - cache.fetchedAt < CACHE_TTL_MS;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
// Model fetching
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
|
|
181
|
-
async function fetchModels(apiKey: string): Promise<RawModel[]> {
|
|
182
|
-
const response = await fetch(`${BASE_URL}/models`, {
|
|
183
|
-
headers: {
|
|
184
|
-
Authorization: `Bearer ${apiKey}`,
|
|
185
|
-
Accept: "application/json",
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
if (!response.ok) {
|
|
190
|
-
throw new Error(`TokenRouter /v1/models returned ${response.status}: ${await response.text()}`);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const payload = (await response.json()) as {
|
|
194
|
-
data: Array<Record<string, unknown>>;
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
return payload.data.map((m) => ({
|
|
198
|
-
id: m.id as string,
|
|
199
|
-
name: (m.name as string | undefined) ?? (m.id as string),
|
|
200
|
-
context_window: (m.context_window ?? m.contextWindow ?? undefined) as number | undefined,
|
|
201
|
-
max_tokens: (m.max_tokens ?? m.maxTokens ?? undefined) as number | undefined,
|
|
202
|
-
}));
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async function getModelsAndPricing(apiKey: string): Promise<{ models: RawModel[]; pricing: Map<string, Pricing> }> {
|
|
206
|
-
const cache = readCache();
|
|
207
|
-
if (cache && isCacheFresh(cache)) {
|
|
208
|
-
const pricingMap = new Map<string, Pricing>(Object.entries(cache.pricing));
|
|
209
|
-
return { models: cache.models, pricing: pricingMap };
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
const [models, pricing] = await Promise.all([
|
|
214
|
-
fetchModels(apiKey),
|
|
215
|
-
fetchOpenRouterPricing(),
|
|
216
|
-
]);
|
|
217
|
-
writeCache(models, pricing);
|
|
218
|
-
return { models, pricing };
|
|
219
|
-
} catch {
|
|
220
|
-
if (cache) {
|
|
221
|
-
const pricingMap = new Map<string, Pricing>(Object.entries(cache.pricing));
|
|
222
|
-
return { models: cache.models, pricing: pricingMap };
|
|
223
|
-
}
|
|
224
|
-
throw new Error(
|
|
225
|
-
"Failed to fetch models from TokenRouter and no cached data available. " +
|
|
226
|
-
"Configure authentication via /login tokenrouter, auth.json, or TOKENROUTER_API_KEY.",
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// ---------------------------------------------------------------------------
|
|
232
|
-
// OAuth (API key prompt flow for /login)
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
|
|
235
|
-
async function loginTokenRouter(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {
|
|
236
|
-
const key = await callbacks.onPrompt({ message: "Enter your TokenRouter API key:" });
|
|
237
|
-
if (!key || !key.trim()) {
|
|
238
|
-
throw new Error("No API key provided");
|
|
239
|
-
}
|
|
240
|
-
// API keys don't expire — set far-future expiry so pi won't try to refresh
|
|
241
|
-
const farFuture = Date.now() + 10 * 365 * 24 * 60 * 60 * 1000;
|
|
242
|
-
return {
|
|
243
|
-
refresh: key.trim(),
|
|
244
|
-
access: key.trim(),
|
|
245
|
-
expires: farFuture,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
async function refreshTokenRouter(credentials: OAuthCredentials): Promise<OAuthCredentials> {
|
|
250
|
-
// API keys don't expire — return as-is
|
|
251
|
-
return credentials;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// ---------------------------------------------------------------------------
|
|
255
|
-
// Extension entry point
|
|
256
|
-
// ---------------------------------------------------------------------------
|
|
257
|
-
|
|
258
|
-
const oauth = {
|
|
259
|
-
name: "TokenRouter",
|
|
260
|
-
login: loginTokenRouter,
|
|
261
|
-
refreshToken: refreshTokenRouter,
|
|
262
|
-
getApiKey: (cred: OAuthCredentials) => cred.access,
|
|
263
|
-
};
|
|
19
|
+
import { TOKENROUTER_MODELS } from "./models.generated.js";
|
|
20
|
+
import { createTokenRouterProviderConfig, PROVIDER_NAME } from "./provider-config.js";
|
|
264
21
|
|
|
265
22
|
export default async function(pi: ExtensionAPI) {
|
|
266
|
-
|
|
267
|
-
// runtime overrides → auth.json api_key → auth.json oauth → env vars
|
|
268
|
-
const authStorage = AuthStorage.create();
|
|
269
|
-
const apiKey = await authStorage.getApiKey(PROVIDER_NAME);
|
|
270
|
-
|
|
271
|
-
if (!apiKey) {
|
|
272
|
-
// Register provider with OAuth only (no models) so /login works.
|
|
273
|
-
// After /login, user needs /reload to fetch and register models.
|
|
274
|
-
pi.registerProvider(PROVIDER_NAME, {
|
|
275
|
-
baseUrl: BASE_URL,
|
|
276
|
-
api: "openai-completions",
|
|
277
|
-
authHeader: true,
|
|
278
|
-
oauth,
|
|
279
|
-
});
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
const { models: rawModels, pricing } = await getModelsAndPricing(apiKey);
|
|
284
|
-
|
|
285
|
-
pi.registerProvider(PROVIDER_NAME, {
|
|
286
|
-
baseUrl: BASE_URL,
|
|
287
|
-
api: "openai-completions",
|
|
288
|
-
authHeader: true,
|
|
289
|
-
models: rawModels.map((m) => {
|
|
290
|
-
const p = pricing.get(m.id);
|
|
291
|
-
return {
|
|
292
|
-
id: m.id,
|
|
293
|
-
name: m.name ?? m.id,
|
|
294
|
-
reasoning: isReasoningModel(m.id),
|
|
295
|
-
input: supportsVision(m.id) ? (["text", "image"] as const) : (["text"] as const),
|
|
296
|
-
cost: p
|
|
297
|
-
? { input: p.input, output: p.output, cacheRead: p.cacheRead, cacheWrite: p.cacheWrite }
|
|
298
|
-
: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
299
|
-
contextWindow: m.context_window ?? p?.contextWindow ?? DEFAULT_CONTEXT_WINDOW,
|
|
300
|
-
maxTokens: m.max_tokens ?? p?.maxTokens ?? DEFAULT_MAX_TOKENS,
|
|
301
|
-
};
|
|
302
|
-
}),
|
|
303
|
-
oauth,
|
|
304
|
-
});
|
|
23
|
+
pi.registerProvider(PROVIDER_NAME, createTokenRouterProviderConfig(TOKENROUTER_MODELS));
|
|
305
24
|
}
|
|
@@ -0,0 +1,1003 @@
|
|
|
1
|
+
import type { TokenRouterProviderModel } from "./provider-config.js";
|
|
2
|
+
|
|
3
|
+
// Derived from TokenRouter /v1/models and enriched with models.dev, then OpenRouter metadata.
|
|
4
|
+
// Generated on 2026-05-07T03:18:45.215Z.
|
|
5
|
+
export const TOKENROUTER_MODELS: TokenRouterProviderModel[] = [
|
|
6
|
+
{
|
|
7
|
+
"id": "claude-haiku-4-5",
|
|
8
|
+
"name": "Claude Haiku 4.5 (latest)",
|
|
9
|
+
"reasoning": true,
|
|
10
|
+
"input": [
|
|
11
|
+
"text",
|
|
12
|
+
"image"
|
|
13
|
+
],
|
|
14
|
+
"cost": {
|
|
15
|
+
"input": 1,
|
|
16
|
+
"output": 5,
|
|
17
|
+
"cacheRead": 0.1,
|
|
18
|
+
"cacheWrite": 1.25
|
|
19
|
+
},
|
|
20
|
+
"contextWindow": 200000,
|
|
21
|
+
"maxTokens": 64000
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "qwen/qwen3.5-9b",
|
|
25
|
+
"name": "Qwen3.5 9B",
|
|
26
|
+
"reasoning": true,
|
|
27
|
+
"input": [
|
|
28
|
+
"text",
|
|
29
|
+
"image"
|
|
30
|
+
],
|
|
31
|
+
"cost": {
|
|
32
|
+
"input": 0.1,
|
|
33
|
+
"output": 0.4,
|
|
34
|
+
"cacheRead": 0,
|
|
35
|
+
"cacheWrite": 0
|
|
36
|
+
},
|
|
37
|
+
"contextWindow": 262144,
|
|
38
|
+
"maxTokens": 262144
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": "xiaomi/mimo-v2.5-pro",
|
|
42
|
+
"name": "MiMo V2.5 Pro",
|
|
43
|
+
"reasoning": true,
|
|
44
|
+
"input": [
|
|
45
|
+
"text"
|
|
46
|
+
],
|
|
47
|
+
"cost": {
|
|
48
|
+
"input": 1,
|
|
49
|
+
"output": 3,
|
|
50
|
+
"cacheRead": 0.19999999999999998,
|
|
51
|
+
"cacheWrite": 0
|
|
52
|
+
},
|
|
53
|
+
"contextWindow": 1050000,
|
|
54
|
+
"maxTokens": 131000
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": "minimax/minimax-m2.7-highspeed",
|
|
58
|
+
"name": "MiniMax M2.7 High Speed",
|
|
59
|
+
"reasoning": true,
|
|
60
|
+
"input": [
|
|
61
|
+
"text",
|
|
62
|
+
"image"
|
|
63
|
+
],
|
|
64
|
+
"cost": {
|
|
65
|
+
"input": 0.6,
|
|
66
|
+
"output": 2.4,
|
|
67
|
+
"cacheRead": 0.06,
|
|
68
|
+
"cacheWrite": 0.375
|
|
69
|
+
},
|
|
70
|
+
"contextWindow": 204800,
|
|
71
|
+
"maxTokens": 131100
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "deepseek/deepseek-v4-pro",
|
|
75
|
+
"name": "DeepSeek V4 Pro",
|
|
76
|
+
"reasoning": true,
|
|
77
|
+
"input": [
|
|
78
|
+
"text"
|
|
79
|
+
],
|
|
80
|
+
"cost": {
|
|
81
|
+
"input": 1.74,
|
|
82
|
+
"output": 3.48,
|
|
83
|
+
"cacheRead": 0.145,
|
|
84
|
+
"cacheWrite": 0
|
|
85
|
+
},
|
|
86
|
+
"contextWindow": 1000000,
|
|
87
|
+
"maxTokens": 384000
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "minimax/minimax-m2.7",
|
|
91
|
+
"name": "Minimax M2.7",
|
|
92
|
+
"reasoning": true,
|
|
93
|
+
"input": [
|
|
94
|
+
"text",
|
|
95
|
+
"image"
|
|
96
|
+
],
|
|
97
|
+
"cost": {
|
|
98
|
+
"input": 0.3,
|
|
99
|
+
"output": 1.2,
|
|
100
|
+
"cacheRead": 0.06,
|
|
101
|
+
"cacheWrite": 0.375
|
|
102
|
+
},
|
|
103
|
+
"contextWindow": 204800,
|
|
104
|
+
"maxTokens": 131000
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"id": "qwen/qwen3.5-122b-a10b",
|
|
108
|
+
"name": "Qwen3.5 122B A10B",
|
|
109
|
+
"reasoning": true,
|
|
110
|
+
"input": [
|
|
111
|
+
"text",
|
|
112
|
+
"image"
|
|
113
|
+
],
|
|
114
|
+
"cost": {
|
|
115
|
+
"input": 0.4,
|
|
116
|
+
"output": 3.2,
|
|
117
|
+
"cacheRead": 0,
|
|
118
|
+
"cacheWrite": 0
|
|
119
|
+
},
|
|
120
|
+
"contextWindow": 262144,
|
|
121
|
+
"maxTokens": 262144
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"id": "xiaomi/mimo-v2.5",
|
|
125
|
+
"name": "MiMo M2.5",
|
|
126
|
+
"reasoning": true,
|
|
127
|
+
"input": [
|
|
128
|
+
"text",
|
|
129
|
+
"image"
|
|
130
|
+
],
|
|
131
|
+
"cost": {
|
|
132
|
+
"input": 0.39999999999999997,
|
|
133
|
+
"output": 2,
|
|
134
|
+
"cacheRead": 0.08,
|
|
135
|
+
"cacheWrite": 0
|
|
136
|
+
},
|
|
137
|
+
"contextWindow": 1050000,
|
|
138
|
+
"maxTokens": 131100
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"id": "qwen/qwen3.5-plus-02-15",
|
|
142
|
+
"name": "Qwen: Qwen3.5 Plus 2026-02-15",
|
|
143
|
+
"reasoning": true,
|
|
144
|
+
"input": [
|
|
145
|
+
"text",
|
|
146
|
+
"image"
|
|
147
|
+
],
|
|
148
|
+
"cost": {
|
|
149
|
+
"input": 0.26,
|
|
150
|
+
"output": 1.56,
|
|
151
|
+
"cacheRead": 0,
|
|
152
|
+
"cacheWrite": 0.325
|
|
153
|
+
},
|
|
154
|
+
"contextWindow": 1000000,
|
|
155
|
+
"maxTokens": 65536
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"id": "x-ai/grok-4.20-beta",
|
|
159
|
+
"name": "Grok 4.20 Beta",
|
|
160
|
+
"reasoning": true,
|
|
161
|
+
"input": [
|
|
162
|
+
"text",
|
|
163
|
+
"image"
|
|
164
|
+
],
|
|
165
|
+
"cost": {
|
|
166
|
+
"input": 2,
|
|
167
|
+
"output": 6,
|
|
168
|
+
"cacheRead": 0.2,
|
|
169
|
+
"cacheWrite": 0
|
|
170
|
+
},
|
|
171
|
+
"contextWindow": 2000000,
|
|
172
|
+
"maxTokens": 30000
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"id": "deepseek/deepseek-v3.2",
|
|
176
|
+
"name": "DeepSeek V3.2",
|
|
177
|
+
"reasoning": true,
|
|
178
|
+
"input": [
|
|
179
|
+
"text"
|
|
180
|
+
],
|
|
181
|
+
"cost": {
|
|
182
|
+
"input": 0.27,
|
|
183
|
+
"output": 0.4,
|
|
184
|
+
"cacheRead": 0.22,
|
|
185
|
+
"cacheWrite": 0
|
|
186
|
+
},
|
|
187
|
+
"contextWindow": 163842,
|
|
188
|
+
"maxTokens": 8000
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": "openai/gpt-5.2",
|
|
192
|
+
"name": "GPT-5.2",
|
|
193
|
+
"reasoning": true,
|
|
194
|
+
"input": [
|
|
195
|
+
"text",
|
|
196
|
+
"image"
|
|
197
|
+
],
|
|
198
|
+
"cost": {
|
|
199
|
+
"input": 1.75,
|
|
200
|
+
"output": 14,
|
|
201
|
+
"cacheRead": 0.18,
|
|
202
|
+
"cacheWrite": 0
|
|
203
|
+
},
|
|
204
|
+
"contextWindow": 400000,
|
|
205
|
+
"maxTokens": 128000
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"id": "anthropic/claude-opus-4.7",
|
|
209
|
+
"name": "Claude Opus 4.7",
|
|
210
|
+
"reasoning": true,
|
|
211
|
+
"input": [
|
|
212
|
+
"text",
|
|
213
|
+
"image"
|
|
214
|
+
],
|
|
215
|
+
"cost": {
|
|
216
|
+
"input": 5,
|
|
217
|
+
"output": 25,
|
|
218
|
+
"cacheRead": 0.5,
|
|
219
|
+
"cacheWrite": 6.25
|
|
220
|
+
},
|
|
221
|
+
"contextWindow": 1000000,
|
|
222
|
+
"maxTokens": 128000
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"id": "qwen/qwen3.5-35b-a3b",
|
|
226
|
+
"name": "Qwen3.5 35B A3B",
|
|
227
|
+
"reasoning": true,
|
|
228
|
+
"input": [
|
|
229
|
+
"text",
|
|
230
|
+
"image"
|
|
231
|
+
],
|
|
232
|
+
"cost": {
|
|
233
|
+
"input": 0.25,
|
|
234
|
+
"output": 1.3,
|
|
235
|
+
"cacheRead": 0.049999999999999996,
|
|
236
|
+
"cacheWrite": 0
|
|
237
|
+
},
|
|
238
|
+
"contextWindow": 262144,
|
|
239
|
+
"maxTokens": 262144
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"id": "anthropic/claude-sonnet-4.5",
|
|
243
|
+
"name": "Claude Sonnet 4.5",
|
|
244
|
+
"reasoning": true,
|
|
245
|
+
"input": [
|
|
246
|
+
"text",
|
|
247
|
+
"image"
|
|
248
|
+
],
|
|
249
|
+
"cost": {
|
|
250
|
+
"input": 3,
|
|
251
|
+
"output": 15,
|
|
252
|
+
"cacheRead": 0.3,
|
|
253
|
+
"cacheWrite": 3.75
|
|
254
|
+
},
|
|
255
|
+
"contextWindow": 200000,
|
|
256
|
+
"maxTokens": 64000
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
"id": "stepfun/step-3.5-flash",
|
|
260
|
+
"name": "StepFun: Step 3.5 Flash",
|
|
261
|
+
"reasoning": true,
|
|
262
|
+
"input": [
|
|
263
|
+
"text"
|
|
264
|
+
],
|
|
265
|
+
"cost": {
|
|
266
|
+
"input": 0.1,
|
|
267
|
+
"output": 0.3,
|
|
268
|
+
"cacheRead": 0.02,
|
|
269
|
+
"cacheWrite": 0
|
|
270
|
+
},
|
|
271
|
+
"contextWindow": 256000,
|
|
272
|
+
"maxTokens": 256000
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"id": "google/gemini-3-flash-preview",
|
|
276
|
+
"name": "Gemini 3 Flash",
|
|
277
|
+
"reasoning": true,
|
|
278
|
+
"input": [
|
|
279
|
+
"text",
|
|
280
|
+
"image"
|
|
281
|
+
],
|
|
282
|
+
"cost": {
|
|
283
|
+
"input": 0.5,
|
|
284
|
+
"output": 3,
|
|
285
|
+
"cacheRead": 0.05,
|
|
286
|
+
"cacheWrite": 1
|
|
287
|
+
},
|
|
288
|
+
"contextWindow": 1048576,
|
|
289
|
+
"maxTokens": 65536
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"id": "z-ai/glm-5-turbo",
|
|
293
|
+
"name": "Z.ai: GLM 5 Turbo",
|
|
294
|
+
"reasoning": true,
|
|
295
|
+
"input": [
|
|
296
|
+
"text"
|
|
297
|
+
],
|
|
298
|
+
"cost": {
|
|
299
|
+
"input": 1.2,
|
|
300
|
+
"output": 4,
|
|
301
|
+
"cacheRead": 0.24,
|
|
302
|
+
"cacheWrite": 0
|
|
303
|
+
},
|
|
304
|
+
"contextWindow": 202752,
|
|
305
|
+
"maxTokens": 131072
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
"id": "openai/gpt-5.4-pro",
|
|
309
|
+
"name": "GPT 5.4 Pro",
|
|
310
|
+
"reasoning": true,
|
|
311
|
+
"input": [
|
|
312
|
+
"text",
|
|
313
|
+
"image"
|
|
314
|
+
],
|
|
315
|
+
"cost": {
|
|
316
|
+
"input": 30,
|
|
317
|
+
"output": 180,
|
|
318
|
+
"cacheRead": 0,
|
|
319
|
+
"cacheWrite": 0
|
|
320
|
+
},
|
|
321
|
+
"contextWindow": 1050000,
|
|
322
|
+
"maxTokens": 128000
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
"id": "qwen/qwen3.5-397b-a17b",
|
|
326
|
+
"name": "Qwen3.5-397B-A17B",
|
|
327
|
+
"reasoning": true,
|
|
328
|
+
"input": [
|
|
329
|
+
"text",
|
|
330
|
+
"image"
|
|
331
|
+
],
|
|
332
|
+
"cost": {
|
|
333
|
+
"input": 0,
|
|
334
|
+
"output": 0,
|
|
335
|
+
"cacheRead": 0.195,
|
|
336
|
+
"cacheWrite": 0
|
|
337
|
+
},
|
|
338
|
+
"contextWindow": 262144,
|
|
339
|
+
"maxTokens": 8192
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"id": "anthropic/claude-opus-4.5",
|
|
343
|
+
"name": "Claude Opus 4.5",
|
|
344
|
+
"reasoning": true,
|
|
345
|
+
"input": [
|
|
346
|
+
"text",
|
|
347
|
+
"image"
|
|
348
|
+
],
|
|
349
|
+
"cost": {
|
|
350
|
+
"input": 5,
|
|
351
|
+
"output": 25,
|
|
352
|
+
"cacheRead": 0.5,
|
|
353
|
+
"cacheWrite": 18.75
|
|
354
|
+
},
|
|
355
|
+
"contextWindow": 200000,
|
|
356
|
+
"maxTokens": 64000
|
|
357
|
+
},
|
|
358
|
+
{
|
|
359
|
+
"id": "mistralai/devstral-2512",
|
|
360
|
+
"name": "Mistral: Devstral 2 2512",
|
|
361
|
+
"reasoning": false,
|
|
362
|
+
"input": [
|
|
363
|
+
"text"
|
|
364
|
+
],
|
|
365
|
+
"cost": {
|
|
366
|
+
"input": 0.4,
|
|
367
|
+
"output": 2,
|
|
368
|
+
"cacheRead": 0.025,
|
|
369
|
+
"cacheWrite": 0
|
|
370
|
+
},
|
|
371
|
+
"contextWindow": 262144,
|
|
372
|
+
"maxTokens": 65536
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"id": "anthropic/claude-sonnet-4",
|
|
376
|
+
"name": "Claude Sonnet 4",
|
|
377
|
+
"reasoning": true,
|
|
378
|
+
"input": [
|
|
379
|
+
"text",
|
|
380
|
+
"image"
|
|
381
|
+
],
|
|
382
|
+
"cost": {
|
|
383
|
+
"input": 3,
|
|
384
|
+
"output": 15,
|
|
385
|
+
"cacheRead": 0.3,
|
|
386
|
+
"cacheWrite": 3.75
|
|
387
|
+
},
|
|
388
|
+
"contextWindow": 200000,
|
|
389
|
+
"maxTokens": 64000
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
"id": "x-ai/grok-4.3",
|
|
393
|
+
"name": "xAI: Grok 4.3",
|
|
394
|
+
"reasoning": true,
|
|
395
|
+
"input": [
|
|
396
|
+
"text",
|
|
397
|
+
"image"
|
|
398
|
+
],
|
|
399
|
+
"cost": {
|
|
400
|
+
"input": 1.25,
|
|
401
|
+
"output": 2.5,
|
|
402
|
+
"cacheRead": 0.2,
|
|
403
|
+
"cacheWrite": 0
|
|
404
|
+
},
|
|
405
|
+
"contextWindow": 1000000,
|
|
406
|
+
"maxTokens": 4096
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
"id": "deepseek-v4-flash",
|
|
410
|
+
"name": "DeepSeek V4 Flash",
|
|
411
|
+
"reasoning": true,
|
|
412
|
+
"input": [
|
|
413
|
+
"text"
|
|
414
|
+
],
|
|
415
|
+
"cost": {
|
|
416
|
+
"input": 0.14,
|
|
417
|
+
"output": 0.28,
|
|
418
|
+
"cacheRead": 0.028,
|
|
419
|
+
"cacheWrite": 0
|
|
420
|
+
},
|
|
421
|
+
"contextWindow": 1000000,
|
|
422
|
+
"maxTokens": 384000
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
"id": "z-ai/glm-5.1",
|
|
426
|
+
"name": "GLM-5.1",
|
|
427
|
+
"reasoning": true,
|
|
428
|
+
"input": [
|
|
429
|
+
"text"
|
|
430
|
+
],
|
|
431
|
+
"cost": {
|
|
432
|
+
"input": 0,
|
|
433
|
+
"output": 0,
|
|
434
|
+
"cacheRead": 0.5249999999999999,
|
|
435
|
+
"cacheWrite": 0
|
|
436
|
+
},
|
|
437
|
+
"contextWindow": 131072,
|
|
438
|
+
"maxTokens": 131072
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
"id": "anthropic/claude-sonnet-4.6",
|
|
442
|
+
"name": "Claude Sonnet 4.6",
|
|
443
|
+
"reasoning": true,
|
|
444
|
+
"input": [
|
|
445
|
+
"text",
|
|
446
|
+
"image"
|
|
447
|
+
],
|
|
448
|
+
"cost": {
|
|
449
|
+
"input": 3,
|
|
450
|
+
"output": 15,
|
|
451
|
+
"cacheRead": 0.3,
|
|
452
|
+
"cacheWrite": 3.75
|
|
453
|
+
},
|
|
454
|
+
"contextWindow": 1000000,
|
|
455
|
+
"maxTokens": 128000
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
"id": "anthropic/claude-haiku-4.5",
|
|
459
|
+
"name": "Claude Haiku 4.5",
|
|
460
|
+
"reasoning": true,
|
|
461
|
+
"input": [
|
|
462
|
+
"text",
|
|
463
|
+
"image"
|
|
464
|
+
],
|
|
465
|
+
"cost": {
|
|
466
|
+
"input": 1,
|
|
467
|
+
"output": 5,
|
|
468
|
+
"cacheRead": 0.1,
|
|
469
|
+
"cacheWrite": 1.25
|
|
470
|
+
},
|
|
471
|
+
"contextWindow": 200000,
|
|
472
|
+
"maxTokens": 64000
|
|
473
|
+
},
|
|
474
|
+
{
|
|
475
|
+
"id": "openai/gpt-5.4",
|
|
476
|
+
"name": "GPT 5.4",
|
|
477
|
+
"reasoning": true,
|
|
478
|
+
"input": [
|
|
479
|
+
"text",
|
|
480
|
+
"image"
|
|
481
|
+
],
|
|
482
|
+
"cost": {
|
|
483
|
+
"input": 2.5,
|
|
484
|
+
"output": 15,
|
|
485
|
+
"cacheRead": 0.25,
|
|
486
|
+
"cacheWrite": 0
|
|
487
|
+
},
|
|
488
|
+
"contextWindow": 1050000,
|
|
489
|
+
"maxTokens": 128000
|
|
490
|
+
},
|
|
491
|
+
{
|
|
492
|
+
"id": "xiaomi/mimo-v2-pro",
|
|
493
|
+
"name": "MiMo V2 Pro",
|
|
494
|
+
"reasoning": true,
|
|
495
|
+
"input": [
|
|
496
|
+
"text"
|
|
497
|
+
],
|
|
498
|
+
"cost": {
|
|
499
|
+
"input": 1,
|
|
500
|
+
"output": 3,
|
|
501
|
+
"cacheRead": 0.19999999999999998,
|
|
502
|
+
"cacheWrite": 0
|
|
503
|
+
},
|
|
504
|
+
"contextWindow": 1000000,
|
|
505
|
+
"maxTokens": 128000
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
"id": "z-ai/glm-4.6v",
|
|
509
|
+
"name": "Z.ai: GLM 4.6V",
|
|
510
|
+
"reasoning": true,
|
|
511
|
+
"input": [
|
|
512
|
+
"text",
|
|
513
|
+
"image"
|
|
514
|
+
],
|
|
515
|
+
"cost": {
|
|
516
|
+
"input": 0.3,
|
|
517
|
+
"output": 0.9,
|
|
518
|
+
"cacheRead": 0.049999999999999996,
|
|
519
|
+
"cacheWrite": 0
|
|
520
|
+
},
|
|
521
|
+
"contextWindow": 131072,
|
|
522
|
+
"maxTokens": 131072
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
"id": "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning:free",
|
|
526
|
+
"name": "NVIDIA: Nemotron 3 Nano Omni (free)",
|
|
527
|
+
"reasoning": true,
|
|
528
|
+
"input": [
|
|
529
|
+
"text",
|
|
530
|
+
"image"
|
|
531
|
+
],
|
|
532
|
+
"cost": {
|
|
533
|
+
"input": 0,
|
|
534
|
+
"output": 0,
|
|
535
|
+
"cacheRead": 0,
|
|
536
|
+
"cacheWrite": 0
|
|
537
|
+
},
|
|
538
|
+
"contextWindow": 256000,
|
|
539
|
+
"maxTokens": 65536
|
|
540
|
+
},
|
|
541
|
+
{
|
|
542
|
+
"id": "deepseek/deepseek-v4-flash",
|
|
543
|
+
"name": "DeepSeek V4 Flash",
|
|
544
|
+
"reasoning": true,
|
|
545
|
+
"input": [
|
|
546
|
+
"text"
|
|
547
|
+
],
|
|
548
|
+
"cost": {
|
|
549
|
+
"input": 0.14,
|
|
550
|
+
"output": 0.28,
|
|
551
|
+
"cacheRead": 0.028,
|
|
552
|
+
"cacheWrite": 0
|
|
553
|
+
},
|
|
554
|
+
"contextWindow": 1000000,
|
|
555
|
+
"maxTokens": 384000
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
"id": "google/gemini-3-pro-image-preview",
|
|
559
|
+
"name": "Google: Nano Banana Pro (Gemini 3 Pro Image Preview)",
|
|
560
|
+
"reasoning": true,
|
|
561
|
+
"input": [
|
|
562
|
+
"text",
|
|
563
|
+
"image"
|
|
564
|
+
],
|
|
565
|
+
"cost": {
|
|
566
|
+
"input": 2,
|
|
567
|
+
"output": 12,
|
|
568
|
+
"cacheRead": 0.19999999999999998,
|
|
569
|
+
"cacheWrite": 0.375
|
|
570
|
+
},
|
|
571
|
+
"contextWindow": 65536,
|
|
572
|
+
"maxTokens": 32768
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
"id": "google/gemini-3.1-flash-image-preview",
|
|
576
|
+
"name": "Gemini 3.1 Flash Image Preview (Nano Banana 2)",
|
|
577
|
+
"reasoning": true,
|
|
578
|
+
"input": [
|
|
579
|
+
"text",
|
|
580
|
+
"image"
|
|
581
|
+
],
|
|
582
|
+
"cost": {
|
|
583
|
+
"input": 0.5,
|
|
584
|
+
"output": 3,
|
|
585
|
+
"cacheRead": 0,
|
|
586
|
+
"cacheWrite": 0
|
|
587
|
+
},
|
|
588
|
+
"contextWindow": 131072,
|
|
589
|
+
"maxTokens": 32768
|
|
590
|
+
},
|
|
591
|
+
{
|
|
592
|
+
"id": "openai/gpt-4o-mini",
|
|
593
|
+
"name": "GPT-4o mini",
|
|
594
|
+
"reasoning": false,
|
|
595
|
+
"input": [
|
|
596
|
+
"text",
|
|
597
|
+
"image"
|
|
598
|
+
],
|
|
599
|
+
"cost": {
|
|
600
|
+
"input": 0,
|
|
601
|
+
"output": 0,
|
|
602
|
+
"cacheRead": 0.075,
|
|
603
|
+
"cacheWrite": 0
|
|
604
|
+
},
|
|
605
|
+
"contextWindow": 128000,
|
|
606
|
+
"maxTokens": 16384
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
"id": "z-ai/glm-4.6",
|
|
610
|
+
"name": "Z.ai: GLM 4.6",
|
|
611
|
+
"reasoning": true,
|
|
612
|
+
"input": [
|
|
613
|
+
"text"
|
|
614
|
+
],
|
|
615
|
+
"cost": {
|
|
616
|
+
"input": 0.39,
|
|
617
|
+
"output": 1.9,
|
|
618
|
+
"cacheRead": 0.175,
|
|
619
|
+
"cacheWrite": 0
|
|
620
|
+
},
|
|
621
|
+
"contextWindow": 204800,
|
|
622
|
+
"maxTokens": 204800
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
"id": "openai/gpt-5.5",
|
|
626
|
+
"name": "GPT 5.5",
|
|
627
|
+
"reasoning": true,
|
|
628
|
+
"input": [
|
|
629
|
+
"text",
|
|
630
|
+
"image"
|
|
631
|
+
],
|
|
632
|
+
"cost": {
|
|
633
|
+
"input": 5,
|
|
634
|
+
"output": 30,
|
|
635
|
+
"cacheRead": 0.5,
|
|
636
|
+
"cacheWrite": 0
|
|
637
|
+
},
|
|
638
|
+
"contextWindow": 1000000,
|
|
639
|
+
"maxTokens": 128000
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
"id": "anthropic/claude-opus-4.6",
|
|
643
|
+
"name": "Claude Opus 4.6",
|
|
644
|
+
"reasoning": true,
|
|
645
|
+
"input": [
|
|
646
|
+
"text",
|
|
647
|
+
"image"
|
|
648
|
+
],
|
|
649
|
+
"cost": {
|
|
650
|
+
"input": 5,
|
|
651
|
+
"output": 25,
|
|
652
|
+
"cacheRead": 0.5,
|
|
653
|
+
"cacheWrite": 6.25
|
|
654
|
+
},
|
|
655
|
+
"contextWindow": 1000000,
|
|
656
|
+
"maxTokens": 128000
|
|
657
|
+
},
|
|
658
|
+
{
|
|
659
|
+
"id": "z-ai/glm-4.7",
|
|
660
|
+
"name": "Z.ai: GLM 4.7",
|
|
661
|
+
"reasoning": true,
|
|
662
|
+
"input": [
|
|
663
|
+
"text"
|
|
664
|
+
],
|
|
665
|
+
"cost": {
|
|
666
|
+
"input": 0.38,
|
|
667
|
+
"output": 1.98,
|
|
668
|
+
"cacheRead": 0.2,
|
|
669
|
+
"cacheWrite": 0
|
|
670
|
+
},
|
|
671
|
+
"contextWindow": 202752,
|
|
672
|
+
"maxTokens": 65535
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
"id": "x-ai/grok-4.1-fast",
|
|
676
|
+
"name": "xAI: Grok 4.1 Fast",
|
|
677
|
+
"reasoning": true,
|
|
678
|
+
"input": [
|
|
679
|
+
"text",
|
|
680
|
+
"image"
|
|
681
|
+
],
|
|
682
|
+
"cost": {
|
|
683
|
+
"input": 0.2,
|
|
684
|
+
"output": 0.5,
|
|
685
|
+
"cacheRead": 0.05,
|
|
686
|
+
"cacheWrite": 0
|
|
687
|
+
},
|
|
688
|
+
"contextWindow": 2000000,
|
|
689
|
+
"maxTokens": 30000
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
"id": "qwen/qwen3-coder-next",
|
|
693
|
+
"name": "Qwen: Qwen3 Coder Next",
|
|
694
|
+
"reasoning": false,
|
|
695
|
+
"input": [
|
|
696
|
+
"text"
|
|
697
|
+
],
|
|
698
|
+
"cost": {
|
|
699
|
+
"input": 0.12,
|
|
700
|
+
"output": 0.75,
|
|
701
|
+
"cacheRead": 0.035,
|
|
702
|
+
"cacheWrite": 0
|
|
703
|
+
},
|
|
704
|
+
"contextWindow": 262144,
|
|
705
|
+
"maxTokens": 65536
|
|
706
|
+
},
|
|
707
|
+
{
|
|
708
|
+
"id": "deepseek-v3.2",
|
|
709
|
+
"name": "DeepSeek-V3.2",
|
|
710
|
+
"reasoning": true,
|
|
711
|
+
"input": [
|
|
712
|
+
"text"
|
|
713
|
+
],
|
|
714
|
+
"cost": {
|
|
715
|
+
"input": 0.58,
|
|
716
|
+
"output": 1.68,
|
|
717
|
+
"cacheRead": 0,
|
|
718
|
+
"cacheWrite": 0
|
|
719
|
+
},
|
|
720
|
+
"contextWindow": 128000,
|
|
721
|
+
"maxTokens": 128000
|
|
722
|
+
},
|
|
723
|
+
{
|
|
724
|
+
"id": "google/gemini-3.1-pro-preview",
|
|
725
|
+
"name": "Gemini 3.1 Pro Preview",
|
|
726
|
+
"reasoning": true,
|
|
727
|
+
"input": [
|
|
728
|
+
"text",
|
|
729
|
+
"image"
|
|
730
|
+
],
|
|
731
|
+
"cost": {
|
|
732
|
+
"input": 2,
|
|
733
|
+
"output": 12,
|
|
734
|
+
"cacheRead": 0.2,
|
|
735
|
+
"cacheWrite": 0.375
|
|
736
|
+
},
|
|
737
|
+
"contextWindow": 1000000,
|
|
738
|
+
"maxTokens": 64000
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
"id": "xiaomi/mimo-v2-flash",
|
|
742
|
+
"name": "MiMo V2 Flash",
|
|
743
|
+
"reasoning": true,
|
|
744
|
+
"input": [
|
|
745
|
+
"text"
|
|
746
|
+
],
|
|
747
|
+
"cost": {
|
|
748
|
+
"input": 0.1,
|
|
749
|
+
"output": 0.29,
|
|
750
|
+
"cacheRead": 0.045,
|
|
751
|
+
"cacheWrite": 0
|
|
752
|
+
},
|
|
753
|
+
"contextWindow": 262144,
|
|
754
|
+
"maxTokens": 32000
|
|
755
|
+
},
|
|
756
|
+
{
|
|
757
|
+
"id": "moonshotai/kimi-k2.6",
|
|
758
|
+
"name": "Kimi K2.6",
|
|
759
|
+
"reasoning": true,
|
|
760
|
+
"input": [
|
|
761
|
+
"text",
|
|
762
|
+
"image"
|
|
763
|
+
],
|
|
764
|
+
"cost": {
|
|
765
|
+
"input": 0.95,
|
|
766
|
+
"output": 4,
|
|
767
|
+
"cacheRead": 0.16,
|
|
768
|
+
"cacheWrite": 0
|
|
769
|
+
},
|
|
770
|
+
"contextWindow": 262000,
|
|
771
|
+
"maxTokens": 262000
|
|
772
|
+
},
|
|
773
|
+
{
|
|
774
|
+
"id": "google/gemini-2.5-flash-image",
|
|
775
|
+
"name": "Nano Banana (Gemini 2.5 Flash Image)",
|
|
776
|
+
"reasoning": false,
|
|
777
|
+
"input": [
|
|
778
|
+
"text",
|
|
779
|
+
"image"
|
|
780
|
+
],
|
|
781
|
+
"cost": {
|
|
782
|
+
"input": 0.3,
|
|
783
|
+
"output": 2.5,
|
|
784
|
+
"cacheRead": 0.03,
|
|
785
|
+
"cacheWrite": 0.08333333333333334
|
|
786
|
+
},
|
|
787
|
+
"contextWindow": 32768,
|
|
788
|
+
"maxTokens": 32768
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
"id": "deepseek-v4-pro",
|
|
792
|
+
"name": "DeepSeek V4 Pro",
|
|
793
|
+
"reasoning": true,
|
|
794
|
+
"input": [
|
|
795
|
+
"text"
|
|
796
|
+
],
|
|
797
|
+
"cost": {
|
|
798
|
+
"input": 1.74,
|
|
799
|
+
"output": 3.48,
|
|
800
|
+
"cacheRead": 0.145,
|
|
801
|
+
"cacheWrite": 0
|
|
802
|
+
},
|
|
803
|
+
"contextWindow": 1000000,
|
|
804
|
+
"maxTokens": 384000
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
"id": "qwen/qwen3.5-flash",
|
|
808
|
+
"name": "Qwen3.5 Flash",
|
|
809
|
+
"reasoning": false,
|
|
810
|
+
"input": [
|
|
811
|
+
"text",
|
|
812
|
+
"image"
|
|
813
|
+
],
|
|
814
|
+
"cost": {
|
|
815
|
+
"input": 0.1,
|
|
816
|
+
"output": 0.4,
|
|
817
|
+
"cacheRead": 0,
|
|
818
|
+
"cacheWrite": 0
|
|
819
|
+
},
|
|
820
|
+
"contextWindow": 1020000,
|
|
821
|
+
"maxTokens": 1020000
|
|
822
|
+
},
|
|
823
|
+
{
|
|
824
|
+
"id": "moonshotai/kimi-k2.5",
|
|
825
|
+
"name": "Kimi K2.5",
|
|
826
|
+
"reasoning": true,
|
|
827
|
+
"input": [
|
|
828
|
+
"text",
|
|
829
|
+
"image"
|
|
830
|
+
],
|
|
831
|
+
"cost": {
|
|
832
|
+
"input": 0.21,
|
|
833
|
+
"output": 1,
|
|
834
|
+
"cacheRead": 0.03,
|
|
835
|
+
"cacheWrite": 0
|
|
836
|
+
},
|
|
837
|
+
"contextWindow": 262144,
|
|
838
|
+
"maxTokens": 262144
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
"id": "z-ai/glm-4.5-air",
|
|
842
|
+
"name": "Z.ai: GLM 4.5 Air",
|
|
843
|
+
"reasoning": true,
|
|
844
|
+
"input": [
|
|
845
|
+
"text"
|
|
846
|
+
],
|
|
847
|
+
"cost": {
|
|
848
|
+
"input": 0.13,
|
|
849
|
+
"output": 0.85,
|
|
850
|
+
"cacheRead": 0.025,
|
|
851
|
+
"cacheWrite": 0
|
|
852
|
+
},
|
|
853
|
+
"contextWindow": 131072,
|
|
854
|
+
"maxTokens": 98304
|
|
855
|
+
},
|
|
856
|
+
{
|
|
857
|
+
"id": "minimax/minimax-m2.5",
|
|
858
|
+
"name": "MiniMax M2.5",
|
|
859
|
+
"reasoning": true,
|
|
860
|
+
"input": [
|
|
861
|
+
"text"
|
|
862
|
+
],
|
|
863
|
+
"cost": {
|
|
864
|
+
"input": 0.14,
|
|
865
|
+
"output": 0.56,
|
|
866
|
+
"cacheRead": 0.014,
|
|
867
|
+
"cacheWrite": 0
|
|
868
|
+
},
|
|
869
|
+
"contextWindow": 1000000,
|
|
870
|
+
"maxTokens": 131072
|
|
871
|
+
},
|
|
872
|
+
{
|
|
873
|
+
"id": "qwen/qwen3.6-plus",
|
|
874
|
+
"name": "Qwen: Qwen3.6 Plus",
|
|
875
|
+
"reasoning": true,
|
|
876
|
+
"input": [
|
|
877
|
+
"text",
|
|
878
|
+
"image"
|
|
879
|
+
],
|
|
880
|
+
"cost": {
|
|
881
|
+
"input": 0.325,
|
|
882
|
+
"output": 1.95,
|
|
883
|
+
"cacheRead": 0.0325,
|
|
884
|
+
"cacheWrite": 0.40625
|
|
885
|
+
},
|
|
886
|
+
"contextWindow": 1000000,
|
|
887
|
+
"maxTokens": 65536
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
"id": "xiaomi/mimo-v2-omni",
|
|
891
|
+
"name": "Xiaomi: MiMo-V2-Omni",
|
|
892
|
+
"reasoning": true,
|
|
893
|
+
"input": [
|
|
894
|
+
"text",
|
|
895
|
+
"image"
|
|
896
|
+
],
|
|
897
|
+
"cost": {
|
|
898
|
+
"input": 0.4,
|
|
899
|
+
"output": 2,
|
|
900
|
+
"cacheRead": 0.08,
|
|
901
|
+
"cacheWrite": 0
|
|
902
|
+
},
|
|
903
|
+
"contextWindow": 262144,
|
|
904
|
+
"maxTokens": 65536
|
|
905
|
+
},
|
|
906
|
+
{
|
|
907
|
+
"id": "openai/gpt-5-mini",
|
|
908
|
+
"name": "GPT-5 Mini",
|
|
909
|
+
"reasoning": true,
|
|
910
|
+
"input": [
|
|
911
|
+
"text",
|
|
912
|
+
"image"
|
|
913
|
+
],
|
|
914
|
+
"cost": {
|
|
915
|
+
"input": 0.25,
|
|
916
|
+
"output": 2,
|
|
917
|
+
"cacheRead": 0.025,
|
|
918
|
+
"cacheWrite": 0
|
|
919
|
+
},
|
|
920
|
+
"contextWindow": 400000,
|
|
921
|
+
"maxTokens": 128000
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
"id": "nvidia/nemotron-3-super-120b-a12b",
|
|
925
|
+
"name": "NVIDIA Nemotron 3 Super 120B A12B",
|
|
926
|
+
"reasoning": true,
|
|
927
|
+
"input": [
|
|
928
|
+
"text"
|
|
929
|
+
],
|
|
930
|
+
"cost": {
|
|
931
|
+
"input": 0.15,
|
|
932
|
+
"output": 0.65,
|
|
933
|
+
"cacheRead": 0,
|
|
934
|
+
"cacheWrite": 0
|
|
935
|
+
},
|
|
936
|
+
"contextWindow": 256000,
|
|
937
|
+
"maxTokens": 32000
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
"id": "minimax/minimax-m2-her",
|
|
941
|
+
"name": "MiniMax: MiniMax M2-her",
|
|
942
|
+
"reasoning": false,
|
|
943
|
+
"input": [
|
|
944
|
+
"text"
|
|
945
|
+
],
|
|
946
|
+
"cost": {
|
|
947
|
+
"input": 0.3,
|
|
948
|
+
"output": 1.2,
|
|
949
|
+
"cacheRead": 0.03,
|
|
950
|
+
"cacheWrite": 0
|
|
951
|
+
},
|
|
952
|
+
"contextWindow": 65536,
|
|
953
|
+
"maxTokens": 2048
|
|
954
|
+
},
|
|
955
|
+
{
|
|
956
|
+
"id": "minimax/minimax-m2.1",
|
|
957
|
+
"name": "MiniMax M2.1",
|
|
958
|
+
"reasoning": true,
|
|
959
|
+
"input": [
|
|
960
|
+
"text"
|
|
961
|
+
],
|
|
962
|
+
"cost": {
|
|
963
|
+
"input": 0.3,
|
|
964
|
+
"output": 1.2,
|
|
965
|
+
"cacheRead": 0.03,
|
|
966
|
+
"cacheWrite": 0.38
|
|
967
|
+
},
|
|
968
|
+
"contextWindow": 204800,
|
|
969
|
+
"maxTokens": 131072
|
|
970
|
+
},
|
|
971
|
+
{
|
|
972
|
+
"id": "minimax/minimax-m2.1-highspeed",
|
|
973
|
+
"name": "minimax/minimax-m2.1-highspeed",
|
|
974
|
+
"reasoning": false,
|
|
975
|
+
"input": [
|
|
976
|
+
"text"
|
|
977
|
+
],
|
|
978
|
+
"cost": {
|
|
979
|
+
"input": 0,
|
|
980
|
+
"output": 0,
|
|
981
|
+
"cacheRead": 0,
|
|
982
|
+
"cacheWrite": 0
|
|
983
|
+
},
|
|
984
|
+
"contextWindow": 4096,
|
|
985
|
+
"maxTokens": 4096
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
"id": "z-ai/glm-5",
|
|
989
|
+
"name": "GLM-5",
|
|
990
|
+
"reasoning": true,
|
|
991
|
+
"input": [
|
|
992
|
+
"text"
|
|
993
|
+
],
|
|
994
|
+
"cost": {
|
|
995
|
+
"input": 0.95,
|
|
996
|
+
"output": 3.15,
|
|
997
|
+
"cacheRead": 0.12,
|
|
998
|
+
"cacheWrite": 0
|
|
999
|
+
},
|
|
1000
|
+
"contextWindow": 204800,
|
|
1001
|
+
"maxTokens": 131072
|
|
1002
|
+
}
|
|
1003
|
+
];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-tokenrouter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "TokenRouter provider extension for pi — dynamic model discovery with OpenRouter-compatible pricing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,9 +19,14 @@
|
|
|
19
19
|
},
|
|
20
20
|
"files": [
|
|
21
21
|
"index.ts",
|
|
22
|
+
"models.generated.ts",
|
|
23
|
+
"provider-config.ts",
|
|
22
24
|
"README.md",
|
|
23
25
|
"LICENSE"
|
|
24
26
|
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"generate-models": "node scripts/generate-models.mjs"
|
|
29
|
+
},
|
|
25
30
|
"pi": {
|
|
26
31
|
"extensions": [
|
|
27
32
|
"./index.ts"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const BASE_URL = "https://api.tokenrouter.com/v1";
|
|
2
|
+
export const PROVIDER_NAME = "tokenrouter";
|
|
3
|
+
export const PROVIDER_DISPLAY_NAME = "TokenRouter";
|
|
4
|
+
export const PROVIDER_API_KEY_ENV = "TOKENROUTER_API_KEY";
|
|
5
|
+
|
|
6
|
+
export type TokenRouterProviderModel = {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
reasoning: boolean;
|
|
10
|
+
input: ("text" | "image")[];
|
|
11
|
+
cost: {
|
|
12
|
+
input: number;
|
|
13
|
+
output: number;
|
|
14
|
+
cacheRead: number;
|
|
15
|
+
cacheWrite: number;
|
|
16
|
+
};
|
|
17
|
+
contextWindow: number;
|
|
18
|
+
maxTokens: number;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function createTokenRouterProviderConfig(models: TokenRouterProviderModel[]) {
|
|
22
|
+
return {
|
|
23
|
+
name: PROVIDER_DISPLAY_NAME,
|
|
24
|
+
baseUrl: BASE_URL,
|
|
25
|
+
api: "openai-completions" as const,
|
|
26
|
+
apiKey: PROVIDER_API_KEY_ENV,
|
|
27
|
+
authHeader: true,
|
|
28
|
+
models,
|
|
29
|
+
};
|
|
30
|
+
}
|