Compare commits

...

66 Commits

Author SHA1 Message Date
Aiden Cline 5ceac8a58b updates 2026-05-19 23:43:01 -05:00
Aiden Cline f107afc57c sync 2026-05-19 22:56:50 -05:00
Aiden Cline 8c677f0134 sync google models 2026-05-19 15:58:06 -05:00
Aiden Cline 462c7877d9 add gemini 3.5 flash 2026-05-19 15:43:38 -05:00
Aiden Cline 55871f9dca Merge pull request #1804 from Adanlink/dev
feat: add deepseek-v4-flash to the fireworks-ai provider
2026-05-19 15:29:33 -05:00
Aiden Cline 4f7194a3c8 test 2026-05-19 15:09:11 -05:00
Aiden Cline f65f0148da add sync guide 2026-05-19 15:08:44 -05:00
Adán 14952f8855 Rename deepseek-v4-flash to deepseek-v4-flash.toml 2026-05-19 18:56:43 +02:00
Adán d7c6d3ad12 Add deepseek-v4-flash model configuration 2026-05-19 18:53:48 +02:00
Aiden Cline 356bc79d08 Merge pull request #1637 from elvexai/fix/amazon-bedrock-kimi-token-limits
fix: Token limits for Amazon Bedrock Kimi K2 models
2026-05-19 09:42:30 -05:00
Aiden Cline a89b1ed726 Merge pull request #1801 from bas3line/sync-routing-run-models
Sync routing.run model catalog
2026-05-19 09:41:38 -05:00
bas3line a998576773 fix(routing-run): match live model metadata 2026-05-19 10:38:58 +05:30
bas3line fbe842bbea fix(routing-run): expose reasoning metadata 2026-05-19 08:39:51 +05:30
bas3line 6c0c3d1b10 fix(routing-run): sync model catalog 2026-05-19 07:37:53 +05:30
Aiden Cline db0a7cf611 Merge pull request #1798 from anomalyco/rework-sync-logic
sync: centralize aggregator model updates
2026-05-18 20:12:50 -05:00
Aiden Cline d775e37e3b Merge pull request #1800 from jerome-benoit/feat/sap-ai-core-gpt-5.4
feat(sap-ai-core): add GPT-5.4
2026-05-18 20:12:21 -05:00
Jérôme Benoit 36753063d9 feat(sap-ai-core): add GPT-5.4
Add gpt-5.4 with availability date from official SAP source.

Drop [[cost.tiers]] from gemini-2.5-pro pending SAP-side tiered
pricing confirmation; sap-ai-core now declares no per-model tiers
(SAP Note 3437766 is login-gated and authoritative for capacity
unit conversion rates).
2026-05-19 02:58:29 +02:00
Aiden Cline 5ee955297a sync: drop vercel catalog updates 2026-05-18 19:07:14 -05:00
Aiden Cline 1b77511903 Merge pull request #1799 from vglafirov/add-gitlab-gpt-5-5
feat(gitlab): add Agentic Chat (GPT-5.5) model
2026-05-18 15:24:18 -05:00
Aiden Cline 8896ead7bf sync: fix vercel pricing tiers 2026-05-18 14:52:40 -05:00
Vladimir Glafirov eb96594d47 feat(gitlab): add Agentic Chat (GPT-5.5) model
Adds duo-chat-gpt-5-5 to the GitLab provider. The GitLab AI Gateway
proxies this model to OpenAI's gpt-5.5-2026-04-23 backend with a
1.05M token context window (922k input + 128k output).

Source: gitlab-org/modelops/applied-ml/code-suggestions/ai-assist
models.yml (gpt_5_5 entry with proxy_provider: openai).

The gitlab-ai-provider npm package exposes this model id starting in
v6.7.0.
2026-05-18 20:44:34 +02:00
Aiden Cline 327332efe3 Merge pull request #1794 from bas3line/add-routing-run-provider
Add routing.run provider
2026-05-18 12:32:34 -05:00
Aiden Cline 5020951745 sync: refresh openrouter after dev merge 2026-05-18 12:30:29 -05:00
Aiden Cline cb6f97774e Merge remote-tracking branch 'origin/dev' into rework-sync-logic 2026-05-18 12:29:28 -05:00
Aiden Cline 7f8b493b0c Merge pull request #1795 from delafthi/delafthi/lxxqxzktnozv
fix(providers/novita-ai): use lowercase model names
2026-05-18 12:28:32 -05:00
Aiden Cline d65a862533 sync: centralize aggregator model updates 2026-05-18 12:12:15 -05:00
Victor Navarro 8e710e19ea bring back old bick-pickle
Added interleaved section with reasoning_content field and removed provider section.
2026-05-18 11:44:14 +02:00
Frank 36c6896e97 update zen models 2026-05-17 22:58:06 -04:00
Aiden Cline a8be548a5d Merge pull request #1416 from Luew2/add-lilac-provider
Add Lilac provider
2026-05-17 19:23:07 -05:00
Luew2 8c2fae4ab0 Keep exact Lilac Gemma model name 2026-05-17 17:19:53 -07:00
Luew2 5e7fad350d Align Lilac Gemma display name 2026-05-17 17:19:04 -07:00
Luew2 feb85ef2c9 Align Lilac provider with registry conventions 2026-05-17 17:15:40 -07:00
Luew2 d4161ebf24 Follow models.dev conventions for Lilac provider 2026-05-17 17:11:03 -07:00
Luew2 b91ab02e2b Add Lilac MiniMax M2.7 model 2026-05-17 17:07:44 -07:00
Luew2 dd09d07f75 Update Lilac Kimi model to K2.6 2026-05-17 17:07:44 -07:00
Luew2 4515f85d47 Add Lilac cache pricing 2026-05-17 17:07:44 -07:00
Luew2 97572240e1 Add Gemma 4 31B IT model
Adds google/gemma-4-31b-it to the Lilac provider ($0.11/M input,
$0.35/M output, 262K context, native multimodal with image/video).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 17:07:44 -07:00
Luew2 80cc04e9e5 Fix Kimi K2.5 output limit to 262,144 tokens 2026-05-17 17:07:44 -07:00
Luew2 d532ebb89b Use purple gradient for Lilac logo (brand colors #6451dc → #b6a6f9) 2026-05-17 17:07:44 -07:00
Luew2 9bae3e887f Replace placeholder logo with Lilac icon mark (currentColor) 2026-05-17 17:07:44 -07:00
Luew2 0be69bf872 Add Lilac provider
Add Lilac as an OpenAI-compatible provider serving:
- z-ai/glm-5.1: Z.ai's flagship agentic model (754B MoE, 202.8K context)
- moonshotai/kimi-k2.5: Moonshot AI's multimodal reasoning model (1T MoE, 262K context)

API: https://api.getlilac.com/v1
Docs: https://docs.getlilac.com
2026-05-17 17:07:44 -07:00
Thierry Delafontaine 0ed38cbecf fix(providers/novita-ai): use lowercase model names
Mixed case naming causes conflicts on case-insensitive filesystems like macOS.
2026-05-17 21:17:46 +02:00
bas3line e65703382a feat: add routing.run provider 2026-05-17 20:27:56 +05:30
Aiden Cline 748754c99b Merge pull request #1792 from monotykamary/fix/neuralwatt-context-limits
fix(neuralwatt): sync context window and output limits with upstream API
2026-05-16 13:14:05 -05:00
Tom X Nguyen ec9c12d0fc fix(neuralwatt): sync context window and output limits with upstream API
Updates all 14 neuralwatt model TOML files with corrected context window
and max output token values as reported by the Neuralwatt API:

- Devstral-Small-2-24B-Instruct-2512: 262,144 -> 262,128
- GLM-5/GLM-5.1 variants: 200,000 -> 202,736
- GPT-OSS-20B: 16,384 -> 16,368
- Kimi-K2.5/K2.6 variants: 262,144 -> 262,128
- MiniMax-M2.5: 196,608 -> 196,592
- Qwen3.5-397B variants: 262,144 -> 262,128
- Qwen3.6-35B variants: 131,072 -> 131,056

Also fixes the README: moves kimi-k2.6-fast from 'Reasoning Models' to
'Fast Variants' and removes incorrect claim that fast variants support
reasoning.
2026-05-16 23:16:34 +07:00
Aiden Cline ac81822c89 Merge pull request #1777 from berget-ai/feat/berget-kimi-k2.6
feat: add Kimi K2.6 to berget.ai
2026-05-16 06:09:00 -05:00
Aiden Cline d32ed764bd Merge pull request #1791 from anomalyco/automation/sync-openrouter-models
Sync OpenRouter models
2026-05-16 06:08:09 -05:00
Christian Landgren 45fb951c42 feat: add Kimi K2.6 to berget.ai
Add Moonshot AI Kimi K2.6 model to berget.ai provider catalog.

- 262K context window
- 16K output tokens
- Text input/output
- Supports: reasoning, structured output, tool calling
- Pricing: /bin/zsh.83/M input, .85/M output (EUR-based)
- Open weights
2026-05-16 12:46:35 +02:00
github-actions[bot] 260d79b2d5 Sync OpenRouter models 2026-05-16 08:54:41 +00:00
Aiden Cline 746b9caf79 Merge pull request #1785 from jerome-benoit/feat/sap-ai-core-opus-4-7
feat(sap-ai-core): add Claude Opus 4.7 and sync model specs
2026-05-15 23:24:47 -05:00
Aiden Cline 8362b55503 Merge pull request #1786 from Ardakilic/chore/kilo-sync-20260516
providers(kilo): sync upstream
2026-05-15 23:24:34 -05:00
Aiden Cline dde3953a9f Merge pull request #1787 from Suat-B/codex/xpersona-www-api-url
Fix Xpersona API base URL
2026-05-15 23:24:06 -05:00
Aiden Cline 0a1695212c Merge pull request #1788 from Jaaneek/xai-may-15-2026-retirement
xai: drop models retired May 15, 2026 + add Grok Imagine models
2026-05-15 23:23:52 -05:00
Jaaneek 89fbb6bb69 xai: drop models retired May 15, 2026 + add Grok Imagine models 2026-05-16 01:57:15 +01:00
SuatB 005fe0fb5a Fix Xpersona provider API URL 2026-05-15 18:37:41 -05:00
Frank e283875ce7 update zen models 2026-05-15 17:24:53 -04:00
Arda Kilicdagi 3598019251 providers(kilo): sync upstream 2026-05-16 01:11:25 +04:00
Jérôme Benoit e9ad8b0a3f feat(sap-ai-core): add Claude Opus 4.7 and sync model specs 2026-05-15 22:16:07 +02:00
Aiden Cline 0ee78eeda5 sync: openrouter models 2026-05-15 10:20:36 -05:00
Aiden Cline 0a5b33e518 Merge pull request #1778 from zhenjunchen-png/add-orcarouter
feat: add OrcaRouter as a new provider
2026-05-15 10:16:29 -05:00
Aiden Cline 22416dda64 Merge pull request #1783 from anomalyco/sync-openrouter
add sync script for openrouter, sync openrouter models
2026-05-15 10:12:02 -05:00
zhenjun.chen 022b1b9946 feat(orcarouter): expand to 80 chat models and add brand logo
Adds 55 additional upstream-mirrored models alongside the existing 25,
covering the full OrcaRouter chat catalog as exposed by
https://www.orcarouter.ai/api/pricing (text-only chat — TTS, embeddings,
video, and image generation are filtered out).

Per-namespace upstream mappings used by [extends]:

  OrcaRouter ns  -> models.dev provider
  ---------------- + ---------------
  openai         -> openai
  anthropic      -> anthropic   (dot version -> dash, e.g. opus-4.7 -> opus-4-7)
  google         -> google
  deepseek       -> deepseek
  qwen           -> alibaba
  grok           -> xai
  kimi           -> moonshotai
  minimax        -> minimax     (minimax-m2.7 -> MiniMax-M2.7)
  z-ai           -> zai

OrcaRouter-specific aliases (dated snapshots like gpt-5-2025-08-07,
search-preview variants, qwen3-vl-* visual variants) are excluded from
v1 because their upstream canonical files do not yet exist in models.dev.

Also adds providers/orcarouter/logo.svg.
2026-05-15 14:48:31 +08:00
zhenjun.chen 699284ce91 chore(orcarouter): drop oversize logo, fall back to models.dev default
The previously committed logo is ~100KB; existing wrapper-provider logos
(openrouter, llmgateway, kilo, aihubmix, ambient) are all 0.3-6KB and use
`currentColor`. Falling back to the default logo per README:

  > If we don't have a provider's logo, a default logo is served instead.

A properly-sized currentColor logo will follow in a separate PR.
2026-05-14 21:37:48 +08:00
zhenjun.chen 7102978cb4 feat: add OrcaRouter provider
OrcaRouter is an OpenAI-compatible meta-router aggregating 150+ LLMs
(OpenAI, Anthropic, Google, xAI, DeepSeek, Qwen, Kimi, MiniMax, ...)
behind a single API key, with a virtual orcarouter/auto smart-routing
entry that picks an upstream per request.

This initial scope covers 26 models (1 AUTO router + 25 upstream mirrors
using [extends]). Pricing computed from https://www.orcarouter.ai/api/pricing
on 2026-05-14: input = model_ratio * $2, output = model_ratio *
completion_ratio * $2 (USD per 1M tokens).

Disclosure: I'm an engineer on the OrcaRouter team.
2026-05-14 20:55:47 +08:00
Mike Sukmanowsky 2cb5a99b98 fix: add model card links for Kimi K2 and Kimi K2.5 2026-04-29 09:29:36 -04:00
Mike Sukmanowsky 0c2e47e8ba Fix token limits for Amazon Bedrock Kimi K2 models
Correct context and output limits for moonshot.kimi-k2-thinking and
moonshotai.kimi-k2.5 on Amazon Bedrock:
- context: 256_000 → 262_143
- output: 256_000 → 16_000
2026-04-28 17:45:59 -04:00
305 changed files with 2333 additions and 1833 deletions
+83
View File
@@ -0,0 +1,83 @@
name: Sync Model Catalogs
on:
schedule:
- cron: "17 8 * * *"
workflow_dispatch:
permissions:
contents: write
issues: write
pull-requests: write
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
sync:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- group: aggregators
title: "chore(sync): update aggregator model catalogs"
branch: automation/sync-models-aggregators
labels: automation,model-sync,sync-group:aggregators,provider:openrouter
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: dev
- name: Setup Bun
uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7
with:
bun-version: latest
- name: Install dependencies
run: bun install
- name: Sync model catalogs
run: bun models:sync ${{ matrix.group }}
env:
OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }}
- name: Validate models
run: bun validate
- name: Create pull request
env:
GH_TOKEN: ${{ github.token }}
BRANCH: ${{ matrix.branch }}
LABELS: ${{ matrix.labels }}
TITLE: ${{ matrix.title }}
run: |
if [ -z "$(git status --porcelain -- providers)" ]; then
echo "No model catalog changes found."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git checkout -B "$BRANCH"
git add providers
git commit -m "$TITLE"
git push --force-with-lease origin "$BRANCH"
label_args=()
IFS=',' read -ra labels <<< "$LABELS"
for label in "${labels[@]}"; do
gh label create "$label" --color "0E8A16" --description "Automated model catalog sync" >/dev/null 2>&1 || true
label_args+=(--label "$label")
done
pr_number="$(gh pr list --head "$BRANCH" --base dev --json number --jq '.[0].number')"
if [ -n "$pr_number" ]; then
gh pr edit "$pr_number" --title "$TITLE" --body-file .sync/model-sync-report.md
for label in "${labels[@]}"; do
gh pr edit "$pr_number" --add-label "$label"
done
else
gh pr create --base dev --head "$BRANCH" --title "$TITLE" --body-file .sync/model-sync-report.md "${label_args[@]}"
fi
+1 -1
View File
@@ -25,7 +25,7 @@
"wandb:generate": "bun ./packages/core/script/generate-wandb.ts",
"digitalocean:generate": "bun ./packages/core/script/generate-digitalocean.ts",
"ambient:generate": "bun ./packages/core/script/generate-ambient.ts",
"openrouter:sync": "bun ./packages/core/script/sync-openrouter.ts"
"models:sync": "bun ./packages/core/script/sync-models.ts"
},
"dependencies": {
"@cloudflare/workers-types": "^4.20260424.1",
+1 -1
View File
@@ -138,7 +138,7 @@ function humanizeModelName(modelId: string): string {
return modelPart.replace(/-/g, " ");
}
// ── Family inference (same approach as generate-vercel.ts) ───────────
// ── Family inference ───────────
function isSubstring(target: string, family: string): boolean {
return target.toLowerCase().includes(family.toLowerCase());
+421
View File
@@ -0,0 +1,421 @@
#!/usr/bin/env bun
import path from "node:path";
import { mkdir, readdir, rm } from "node:fs/promises";
import { z } from "zod";
import { AuthoredModel, AuthoredModelShape } from "../src/schema.js";
import { google } from "./sync/google.js";
import { openrouter } from "./sync/openrouter.js";
const ExistingModel = AuthoredModelShape.partial()
.extend({
extends: z
.object({
from: z.string(),
omit: z.array(z.string()).optional(),
})
.strict()
.optional(),
})
.strict();
export type ExistingModel = z.infer<typeof ExistingModel>;
export type SyncedModel = Omit<z.infer<typeof AuthoredModelShape>, "id">;
export interface SyncProvider<SourceModel> {
id: string;
name: string;
modelsDir: string;
skipCreates?: boolean;
sourceID?(model: SourceModel): string;
skippedNotice?(ids: string[]): string[];
fetchModels(): Promise<unknown>;
parseModels(raw: unknown): SourceModel[];
translateModel(
model: SourceModel,
context: { existing(id: string): ExistingModel | undefined },
): { id: string; model: SyncedModel } | undefined;
}
export interface SyncResult {
id: string;
name: string;
status: "changed" | "unchanged";
created: number;
updated: number;
deleted: number;
unchanged: number;
notices: string[];
files: Array<{ status: "created" | "updated" | "deleted"; path: string }>;
}
export const providers: {
google: SyncProvider<any>;
openrouter: SyncProvider<any>;
} = {
google,
openrouter,
};
export const groups = {
aggregators: ["openrouter"],
direct: ["google"],
} as const;
type ProviderID = keyof typeof providers;
interface SyncOptions {
dryRun?: boolean;
newOnly?: boolean;
}
export async function syncProviderByID(id: ProviderID, options: SyncOptions = {}) {
return syncProvider(providers[id], options);
}
export async function syncProvider<SourceModel>(
provider: SyncProvider<SourceModel>,
options: SyncOptions = {},
): Promise<SyncResult> {
console.log(`\nSyncing ${provider.name}...`);
const existing = await readExisting(provider.modelsDir);
const sourceModels = provider.parseModels(await provider.fetchModels());
const desired = new Map<string, { model: z.infer<typeof AuthoredModel>; content: string }>();
const skippedRemote: string[] = [];
for (const sourceModel of sourceModels) {
const translated = provider.translateModel(sourceModel, {
existing(id) {
return existing.get(`${id}.toml`)?.toml;
},
});
if (translated === undefined) {
if (provider.skipCreates) skippedRemote.push(provider.sourceID?.(sourceModel) ?? "unknown");
continue;
}
const relativePath = `${translated.id}.toml`;
if (provider.skipCreates && !existing.has(relativePath)) {
skippedRemote.push(translated.id);
continue;
}
if (desired.has(relativePath)) {
throw new Error(`Duplicate synced model path: ${provider.id}/${relativePath}`);
}
const parsed = AuthoredModel.safeParse({
id: translated.id,
...translated.model,
});
if (!parsed.success) {
parsed.error.cause = { provider: provider.id, path: relativePath };
throw parsed.error;
}
desired.set(relativePath, {
model: parsed.data,
content: formatToml(parsed.data),
});
}
const files: SyncResult["files"] = [];
let unchanged = 0;
for (const [relativePath, file] of desired) {
const filePath = path.join(provider.modelsDir, relativePath);
const current = existing.get(relativePath);
if (current === undefined) {
files.push({ status: "created", path: filePath });
if (options.dryRun) {
console.log(`Would create ${relativePath}`);
} else {
await mkdir(path.dirname(filePath), { recursive: true });
await Bun.write(filePath, file.content);
}
continue;
}
if (!sameModel(relativePath, current.toml, file.model)) {
if (options.newOnly) {
unchanged++;
continue;
}
files.push({ status: "updated", path: filePath });
if (options.dryRun) {
console.log(`Would update ${relativePath}`);
} else {
if (current.symlink) await rm(filePath, { force: true });
await Bun.write(filePath, file.content);
}
} else {
unchanged++;
}
}
for (const relativePath of existing.keys()) {
if (desired.has(relativePath)) continue;
if (options.newOnly) {
console.log(`Skipping removal in new-only mode: ${relativePath}`);
unchanged++;
continue;
}
const filePath = path.join(provider.modelsDir, relativePath);
files.push({ status: "deleted", path: filePath });
if (options.dryRun) {
console.log(`Would remove ${relativePath}`);
} else {
await rm(filePath, { force: true });
}
}
const result = summarize(provider, files, unchanged, provider.skippedNotice?.(skippedRemote) ?? []);
console.log(
`${options.dryRun ? "Dry run: " : ""}${result.created} created, ${result.updated} updated, ${result.deleted} removed, ${result.unchanged} unchanged`,
);
return result;
}
export async function syncTargets(target: string, options: SyncOptions = {}) {
const ids = target in groups
? groups[target as keyof typeof groups]
: target in providers
? [target as ProviderID]
: undefined;
if (ids === undefined) {
throw new Error(`Unknown sync target: ${target}`);
}
const results: SyncResult[] = [];
for (const id of ids) {
results.push(await syncProviderByID(id as ProviderID, options));
}
return results;
}
async function readExisting(modelsDir: string) {
const existing = new Map<string, { text: string; toml: ExistingModel; symlink: boolean }>();
for (const { file, symlink } of await tomlFiles(modelsDir)) {
const text = await Bun.file(path.join(modelsDir, file)).text();
const parsed = ExistingModel.safeParse(Bun.TOML.parse(text));
if (!parsed.success) {
parsed.error.cause = { path: path.join(modelsDir, file) };
throw parsed.error;
}
existing.set(file, { text, toml: parsed.data, symlink });
}
return existing;
}
async function tomlFiles(root: string, dir = "") {
const result: Array<{ file: string; symlink: boolean }> = [];
for (const entry of await readdir(path.join(root, dir), { withFileTypes: true })) {
const file = path.join(dir, entry.name);
if (entry.isDirectory()) {
result.push(...await tomlFiles(root, file));
} else if (entry.name.endsWith(".toml") && (entry.isFile() || entry.isSymbolicLink())) {
result.push({ file, symlink: entry.isSymbolicLink() });
}
}
return result;
}
function summarize(
provider: { id: string; name: string },
files: SyncResult["files"],
unchanged: number,
notices: string[],
): SyncResult {
return {
id: provider.id,
name: provider.name,
status: files.length > 0 ? "changed" : "unchanged",
created: files.filter((file) => file.status === "created").length,
updated: files.filter((file) => file.status === "updated").length,
deleted: files.filter((file) => file.status === "deleted").length,
unchanged,
notices,
files,
};
}
function sameModel(
relativePath: string,
current: ExistingModel,
desired: z.infer<typeof AuthoredModel>,
) {
const parsed = AuthoredModel.safeParse({
id: relativePath.slice(0, -5),
...current,
});
return parsed.success && stable(parsed.data) === stable(desired);
}
function stable(value: unknown): string {
if (Array.isArray(value)) {
const items = value.map(stable);
const ordered = value.every((item) => item === null || typeof item !== "object")
? items.sort()
: items;
return `[${ordered.join(",")}]`;
}
if (value !== null && typeof value === "object") {
return `{${Object.entries(value)
.filter(([, item]) => item !== undefined)
.sort(([a], [b]) => a.localeCompare(b))
.map(([key, item]) => `${JSON.stringify(key)}:${stable(item)}`)
.join(",")}}`;
}
return JSON.stringify(value);
}
async function writeReport(target: string, results: SyncResult[]) {
await mkdir(".sync", { recursive: true });
const lines = [
`Updates model TOMLs for the \`${target}\` sync target.`,
"",
"| Provider | Status | Created | Updated | Deleted |",
"| --- | --- | ---: | ---: | ---: |",
];
for (const result of results) {
lines.push(
`| ${result.name} | ${result.status} | ${result.created} | ${result.updated} | ${result.deleted} |`,
);
}
for (const result of results.filter((item) => item.files.length > 0)) {
lines.push("", `<details><summary>${result.name} changed files</summary>`, "");
for (const file of result.files) {
lines.push(`- ${file.status}: \`${file.path}\``);
}
lines.push("", "</details>");
}
const noticeResults = results.filter((item) => item.notices.length > 0);
if (noticeResults.length > 0) {
lines.push("", "## Notices");
for (const result of noticeResults) {
lines.push("", `### ${result.name}`);
for (const notice of result.notices) {
lines.push(`- ${notice}`);
}
}
}
lines.push("", "This PR was created automatically by the daily model sync workflow.");
await Bun.write(".sync/model-sync-report.md", `${lines.join("\n")}\n`);
}
function quote(value: string) {
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
}
function formatInteger(n: number) {
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, "_");
}
function formatNumber(n: number) {
return Number.isInteger(n) ? formatInteger(n) : String(n);
}
function formatToml(model: z.infer<typeof AuthoredModel>) {
const lines: string[] = [];
lines.push(`name = ${quote(model.name)}`);
if (model.family !== undefined) lines.push(`family = ${quote(model.family)}`);
lines.push(`release_date = ${quote(model.release_date)}`);
lines.push(`last_updated = ${quote(model.last_updated)}`);
lines.push(`attachment = ${model.attachment}`);
lines.push(`reasoning = ${model.reasoning}`);
if (model.temperature !== undefined) lines.push(`temperature = ${model.temperature}`);
lines.push(`tool_call = ${model.tool_call}`);
if (model.structured_output !== undefined) {
lines.push(`structured_output = ${model.structured_output}`);
}
if (model.knowledge !== undefined) lines.push(`knowledge = ${quote(model.knowledge)}`);
lines.push(`open_weights = ${model.open_weights}`);
if (model.status !== undefined) lines.push(`status = ${quote(model.status)}`);
if (model.interleaved !== undefined) {
lines.push("");
if (model.interleaved === true) {
lines.push("interleaved = true");
} else {
lines.push("[interleaved]");
lines.push(`field = ${quote(model.interleaved.field)}`);
}
}
if (model.cost !== undefined) {
lines.push("", "[cost]");
lines.push(`input = ${formatNumber(model.cost.input)}`);
lines.push(`output = ${formatNumber(model.cost.output)}`);
if (model.cost.reasoning !== undefined) {
lines.push(`reasoning = ${formatNumber(model.cost.reasoning)}`);
}
if (model.cost.cache_read !== undefined) {
lines.push(`cache_read = ${formatNumber(model.cost.cache_read)}`);
}
if (model.cost.cache_write !== undefined) {
lines.push(`cache_write = ${formatNumber(model.cost.cache_write)}`);
}
if (model.cost.input_audio !== undefined) {
lines.push(`input_audio = ${formatNumber(model.cost.input_audio)}`);
}
if (model.cost.output_audio !== undefined) {
lines.push(`output_audio = ${formatNumber(model.cost.output_audio)}`);
}
for (const tier of model.cost.tiers ?? []) {
lines.push("", "[[cost.tiers]]");
lines.push(`tier = { size = ${formatInteger(tier.tier.size)} }`);
lines.push(`input = ${formatNumber(tier.input)}`);
lines.push(`output = ${formatNumber(tier.output)}`);
if (tier.reasoning !== undefined) lines.push(`reasoning = ${formatNumber(tier.reasoning)}`);
if (tier.cache_read !== undefined) lines.push(`cache_read = ${formatNumber(tier.cache_read)}`);
if (tier.cache_write !== undefined) lines.push(`cache_write = ${formatNumber(tier.cache_write)}`);
}
}
lines.push("", "[limit]");
lines.push(`context = ${formatInteger(model.limit.context)}`);
if (model.limit.input !== undefined) lines.push(`input = ${formatInteger(model.limit.input)}`);
lines.push(`output = ${formatInteger(model.limit.output)}`);
lines.push("", "[modalities]");
lines.push(`input = [${model.modalities.input.map(quote).join(", ")}]`);
lines.push(`output = [${model.modalities.output.map(quote).join(", ")}]`);
return `${lines.join("\n")}\n`;
}
export async function main(args = process.argv.slice(2)) {
const target = args.find((arg) => !arg.startsWith("-")) ?? "aggregators";
const results = await syncTargets(target, {
dryRun: args.includes("--dry-run"),
newOnly: args.includes("--new-only"),
});
await writeReport(target, results);
console.log("\nSync summary");
for (const result of results) {
console.log(
`${result.name}: ${result.created} created, ${result.updated} updated, ${result.deleted} deleted`,
);
}
}
if (import.meta.main) await main();
-318
View File
@@ -1,318 +0,0 @@
#!/usr/bin/env bun
import path from "node:path";
import { mkdir, rm } from "node:fs/promises";
import { z } from "zod";
import { ModelFamilyValues } from "../src/family.js";
import { AuthoredModel, AuthoredModelShape } from "../src/schema.js";
const API_ENDPOINT = "https://openrouter.ai/api/v1/models";
const OpenRouterModel = z
.object({
id: z.string(),
name: z.string(),
created: z.number(),
hugging_face_id: z.string().nullable(),
knowledge_cutoff: z.string().nullable(),
context_length: z.number(),
architecture: z.object({
input_modalities: z.array(z.string()),
output_modalities: z.array(z.string()),
}),
pricing: z
.object({
prompt: z.string(),
completion: z.string(),
internal_reasoning: z.string().optional(),
input_cache_read: z.string().optional(),
input_cache_write: z.string().optional(),
}),
top_provider: z.object({
context_length: z.number().nullable(),
max_completion_tokens: z.number().nullable(),
}),
supported_parameters: z.array(z.string()),
});
const OpenRouterResponse = z
.object({
data: z.array(OpenRouterModel),
})
.passthrough();
const ExistingModel = AuthoredModelShape.partial()
.extend({
extends: z
.object({
from: z.string(),
omit: z.array(z.string()).optional(),
})
.strict()
.optional(),
})
.strict();
type OpenRouterModel = z.infer<typeof OpenRouterModel>;
type ExistingModel = z.infer<typeof ExistingModel>;
function dateFromTimestamp(timestamp: number | undefined) {
if (timestamp === undefined) return new Date().toISOString().slice(0, 10);
return new Date(timestamp * 1000).toISOString().slice(0, 10);
}
function formatInteger(n: number) {
return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, "_");
}
function quote(value: string) {
return `"${value.replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
}
function price(value: string | undefined) {
if (value === undefined) return undefined;
const number = Number(value);
return Number.isFinite(number) && number >= 0
? Math.round(number * 1_000_000_000_000) / 1_000_000
: undefined;
}
function modality(value: string) {
return value === "file" ? "pdf" : value;
}
function modalities(values: string[] | undefined, fallback: string[]) {
const allowed = new Set(["text", "audio", "image", "video", "pdf"]);
const result = (values ?? fallback)
.map((value) => modality(value.toLowerCase()))
.filter((value) => allowed.has(value));
return [...new Set(result.length > 0 ? result : fallback)];
}
function inferFamily(model: OpenRouterModel, name: string) {
const target = `${model.id} ${name}`.toLowerCase();
return [...ModelFamilyValues]
.sort((a, b) => b.length - a.length)
.find((family) => {
const value = family.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
if (family === "o") {
return new RegExp(`(^|[^a-z0-9])${value}(?=\\d|$|[^a-z0-9])`).test(target);
}
return new RegExp(`(^|[^a-z0-9])${value}(?=$|[^a-z0-9])`).test(target);
});
}
function buildModel(model: OpenRouterModel, existing: ExistingModel | undefined) {
const params = new Set(model.supported_parameters ?? []);
const name = model.name.replace(/^[^:]+:\s+/, "");
const input = modalities(model.architecture?.input_modalities, ["text"]);
const output = modalities(model.architecture?.output_modalities, ["text"]);
const prompt = price(model.pricing?.prompt);
const completion = price(model.pricing?.completion);
const reasoning = params.has("reasoning") || params.has("include_reasoning");
const context = model.top_provider?.context_length ?? model.context_length ?? 0;
const maxOutput = model.top_provider?.max_completion_tokens ?? existing?.limit?.output ?? context;
const family = inferFamily(model, name);
return {
name,
family: existing?.family === "o" && family !== "o"
? family
: (existing?.family ?? family),
release_date: dateFromTimestamp(model.created),
last_updated: dateFromTimestamp(model.created),
attachment: input.some((value) => value !== "text"),
reasoning,
temperature: params.has("temperature"),
tool_call: params.has("tools") || params.has("tool_choice"),
structured_output:
params.has("structured_outputs") || params.has("response_format"),
knowledge: model.knowledge_cutoff?.slice(0, 10) ?? existing?.knowledge,
open_weights: Boolean(model.hugging_face_id),
status: existing?.status,
interleaved: existing?.interleaved,
cost:
prompt !== undefined && completion !== undefined
? {
input: prompt,
output: completion,
reasoning: reasoning ? price(model.pricing?.internal_reasoning) : undefined,
cache_read: price(model.pricing?.input_cache_read),
cache_write: price(model.pricing?.input_cache_write),
tiers: existing?.cost?.tiers,
}
: existing?.cost,
limit: {
context,
input: existing?.limit?.input,
output: maxOutput,
},
modalities: { input, output },
};
}
function formatToml(model: ReturnType<typeof buildModel>) {
const lines: string[] = [];
lines.push(`name = ${quote(model.name)}`);
if (model.family) lines.push(`family = ${quote(model.family)}`);
lines.push(`release_date = ${quote(model.release_date)}`);
lines.push(`last_updated = ${quote(model.last_updated)}`);
lines.push(`attachment = ${model.attachment}`);
lines.push(`reasoning = ${model.reasoning}`);
lines.push(`temperature = ${model.temperature}`);
lines.push(`tool_call = ${model.tool_call}`);
lines.push(`structured_output = ${model.structured_output}`);
if (model.knowledge) lines.push(`knowledge = ${quote(model.knowledge)}`);
lines.push(`open_weights = ${model.open_weights}`);
if (model.status) lines.push(`status = ${quote(model.status)}`);
if (model.interleaved !== undefined) {
lines.push("");
if (model.interleaved === true) {
lines.push("interleaved = true");
} else {
lines.push("[interleaved]");
lines.push(`field = ${quote(model.interleaved.field)}`);
}
}
if (model.cost) {
lines.push("");
lines.push("[cost]");
if (model.cost.input !== undefined) lines.push(`input = ${model.cost.input}`);
if (model.cost.output !== undefined) lines.push(`output = ${model.cost.output}`);
if (model.cost.reasoning !== undefined) {
lines.push(`reasoning = ${model.cost.reasoning}`);
}
if (model.cost.cache_read !== undefined) {
lines.push(`cache_read = ${model.cost.cache_read}`);
}
if (model.cost.cache_write !== undefined) {
lines.push(`cache_write = ${model.cost.cache_write}`);
}
for (const tier of model.cost.tiers ?? []) {
lines.push("");
lines.push("[[cost.tiers]]");
lines.push(`tier = { size = ${formatInteger(tier.tier.size)} }`);
if (tier.input !== undefined) lines.push(`input = ${tier.input}`);
if (tier.output !== undefined) lines.push(`output = ${tier.output}`);
if (tier.cache_read !== undefined) lines.push(`cache_read = ${tier.cache_read}`);
if (tier.cache_write !== undefined) lines.push(`cache_write = ${tier.cache_write}`);
}
}
lines.push("");
lines.push("[limit]");
lines.push(`context = ${formatInteger(model.limit.context)}`);
if (model.limit.input !== undefined) {
lines.push(`input = ${formatInteger(model.limit.input)}`);
}
lines.push(`output = ${formatInteger(model.limit.output)}`);
lines.push("");
lines.push("[modalities]");
lines.push(`input = [${model.modalities.input.map(quote).join(", ")}]`);
lines.push(`output = [${model.modalities.output.map(quote).join(", ")}]`);
return `${lines.join("\n")}\n`;
}
async function main() {
const dryRun = process.argv.includes("--dry-run");
const modelsDir = path.join(
import.meta.dirname,
"..",
"..",
"..",
"providers",
"openrouter",
"models",
);
const headers = process.env.OPENROUTER_API_KEY
? { Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}` }
: undefined;
const response = await fetch(API_ENDPOINT, { headers });
if (!response.ok) {
throw new Error(`OpenRouter request failed: ${response.status} ${response.statusText}`);
}
const parsed = OpenRouterResponse.safeParse(await response.json());
if (!parsed.success) throw parsed.error;
const existingFiles = new Set<string>();
for await (const file of new Bun.Glob("**/*.toml").scan({ cwd: modelsDir })) {
existingFiles.add(file);
}
let created = 0;
let updated = 0;
let removed = 0;
let unchanged = 0;
const apiFiles = new Set<string>();
for (const apiModel of parsed.data.data) {
const relativePath = `${apiModel.id}.toml`;
const filePath = path.join(modelsDir, relativePath);
const file = Bun.file(filePath);
const current = await file.exists() ? await file.text() : undefined;
const existing = current === undefined
? undefined
: ExistingModel.parse(Bun.TOML.parse(current));
const model = buildModel(apiModel, existing);
const next = formatToml(model);
const valid = AuthoredModel.safeParse({
id: relativePath.slice(0, -5),
...Bun.TOML.parse(next),
});
if (!valid.success) {
valid.error.cause = { relativePath };
throw valid.error;
}
apiFiles.add(relativePath);
if (current === undefined) {
created++;
if (dryRun) {
console.log(`Would create ${relativePath}`);
} else {
await mkdir(path.dirname(filePath), { recursive: true });
await Bun.write(filePath, next);
}
continue;
}
if (current !== next) {
updated++;
if (dryRun) {
console.log(`Would update ${relativePath}`);
} else {
await Bun.write(filePath, next);
}
} else {
unchanged++;
}
}
for (const relativePath of existingFiles) {
if (apiFiles.has(relativePath)) continue;
removed++;
if (dryRun) {
console.log(`Would remove ${relativePath}`);
} else {
await rm(path.join(modelsDir, relativePath));
}
}
console.log(
`${dryRun ? "Dry run: " : ""}${created} created, ${updated} updated, ${removed} removed, ${unchanged} unchanged`,
);
}
await main();
+138
View File
@@ -0,0 +1,138 @@
import { z } from "zod";
import type { ExistingModel, SyncProvider, SyncedModel } from "../sync-models.js";
const API_ENDPOINT = "https://generativelanguage.googleapis.com/v1beta/models";
const GoogleModel = z.object({
name: z.string(),
baseModelId: z.string().optional(),
version: z.string().optional(),
displayName: z.string().optional(),
description: z.string().optional(),
inputTokenLimit: z.number().int().nonnegative(),
outputTokenLimit: z.number().int().nonnegative(),
supportedGenerationMethods: z.array(z.string()).optional(),
temperature: z.number().optional(),
topP: z.number().optional(),
topK: z.number().optional(),
maxTemperature: z.number().optional(),
thinking: z.boolean().optional(),
}).passthrough();
const GoogleResponse = z.object({
models: z.array(GoogleModel).optional(),
nextPageToken: z.string().optional(),
}).passthrough();
type GoogleModel = z.infer<typeof GoogleModel>;
export const google = {
id: "google",
name: "Google",
modelsDir: "providers/google/models",
skipCreates: true,
sourceID(model) {
return model.name.replace(/^models\//, "");
},
skippedNotice(ids) {
if (ids.length === 0) return [];
return [
`${ids.length} Google models returned by the API were not created because the Models API does not provide authoritative modalities, pricing, knowledge cutoff, release date, tool calling, or structured output metadata. Existing models are still updated from API-authoritative fields.`,
`Skipped remote IDs: ${ids.map((id) => `\`${id}\``).join(", ")}`,
];
},
async fetchModels() {
const key = process.env.GOOGLE_API_KEY
?? process.env.GEMINI_API_KEY
?? process.env.GOOGLE_GENERATIVE_AI_API_KEY;
if (key === undefined) {
throw new Error("Google sync requires GOOGLE_API_KEY, GEMINI_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY");
}
const models: GoogleModel[] = [];
let pageToken: string | undefined;
do {
const url = new URL(API_ENDPOINT);
url.searchParams.set("key", key);
url.searchParams.set("pageSize", "1000");
if (pageToken !== undefined) url.searchParams.set("pageToken", pageToken);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Google models request failed: ${response.status} ${response.statusText}`);
}
const page = GoogleResponse.parse(await response.json());
models.push(...page.models ?? []);
pageToken = page.nextPageToken;
} while (pageToken !== undefined);
return { models };
},
parseModels(raw) {
return GoogleResponse.parse(raw).models ?? [];
},
translateModel(model, context) {
const id = model.name.replace(/^models\//, "");
const existing = context.existing(id);
if (existing === undefined) return undefined;
return {
id,
model: buildModel(model, existing),
};
},
} satisfies SyncProvider<GoogleModel>;
function buildModel(model: GoogleModel, existing: ExistingModel): SyncedModel {
const name = existing.name;
const releaseDate = existing.release_date;
const lastUpdated = existing.last_updated;
const attachment = existing.attachment;
const reasoning = existing.reasoning;
const toolCall = existing.tool_call;
const openWeights = existing.open_weights;
const limit = existing.limit;
const modalities = existing.modalities;
if (
name === undefined
|| releaseDate === undefined
|| lastUpdated === undefined
|| attachment === undefined
|| reasoning === undefined
|| toolCall === undefined
|| openWeights === undefined
|| limit === undefined
|| modalities === undefined
) {
throw new Error(`Google model ${model.name} has incomplete local TOML metadata required for sync`);
}
return {
name: model.displayName ?? name,
family: existing.family,
release_date: releaseDate,
last_updated: lastUpdated,
attachment,
reasoning: model.thinking ?? reasoning,
temperature: model.temperature !== undefined || model.maxTemperature !== undefined
? true
: existing.temperature,
tool_call: toolCall,
structured_output: existing.structured_output,
knowledge: existing.knowledge,
open_weights: openWeights,
status: existing.status,
interleaved: existing.interleaved,
cost: existing.cost,
limit: {
input: limit.input,
context: model.inputTokenLimit,
output: model.outputTokenLimit,
},
modalities,
};
}
+144
View File
@@ -0,0 +1,144 @@
import { z } from "zod";
import { ModelFamilyValues } from "../../src/family.js";
import type { ExistingModel, SyncProvider } from "../sync-models.js";
const API_ENDPOINT = "https://openrouter.ai/api/v1/models";
const OpenRouterModel = z.object({
id: z.string(),
name: z.string(),
created: z.number(),
hugging_face_id: z.string().nullable(),
knowledge_cutoff: z.string().nullable(),
context_length: z.number(),
architecture: z.object({
input_modalities: z.array(z.string()),
output_modalities: z.array(z.string()),
}),
pricing: z.object({
prompt: z.string(),
completion: z.string(),
internal_reasoning: z.string().optional(),
input_cache_read: z.string().optional(),
input_cache_write: z.string().optional(),
}),
top_provider: z.object({
context_length: z.number().nullable(),
max_completion_tokens: z.number().nullable(),
}),
supported_parameters: z.array(z.string()),
});
const OpenRouterResponse = z.object({
data: z.array(OpenRouterModel),
}).passthrough();
type OpenRouterModel = z.infer<typeof OpenRouterModel>;
export const openrouter = {
id: "openrouter",
name: "OpenRouter",
modelsDir: "providers/openrouter/models",
async fetchModels() {
const headers = process.env.OPENROUTER_API_KEY
? { Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}` }
: undefined;
const response = await fetch(API_ENDPOINT, { headers });
if (!response.ok) {
throw new Error(`OpenRouter request failed: ${response.status} ${response.statusText}`);
}
return response.json();
},
parseModels(raw) {
return OpenRouterResponse.parse(raw).data;
},
translateModel(model, context) {
return {
id: model.id,
model: buildModel(model, context.existing(model.id)),
};
},
} satisfies SyncProvider<OpenRouterModel>;
function dateFromTimestamp(timestamp: number) {
return new Date(timestamp * 1000).toISOString().slice(0, 10);
}
function price(value: string | undefined) {
if (value === undefined) return undefined;
const number = Number(value);
return Number.isFinite(number) && number >= 0
? Math.round(number * 1_000_000_000_000) / 1_000_000
: undefined;
}
type Modality = "text" | "audio" | "image" | "video" | "pdf";
function modalities(values: string[], fallback: Modality[]): Modality[] {
const allowed = new Set<Modality>(["text", "audio", "image", "video", "pdf"]);
const result = values
.map((value) => value.toLowerCase())
.map((value) => value === "file" ? "pdf" : value)
.filter((value): value is Modality => allowed.has(value as Modality));
return [...new Set(result.length > 0 ? result : fallback)];
}
function inferFamily(model: OpenRouterModel, name: string) {
const target = `${model.id} ${name}`.toLowerCase();
return [...ModelFamilyValues]
.sort((a, b) => b.length - a.length)
.find((family) => {
const value = family.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
if (family === "o") {
return new RegExp(`(^|[^a-z0-9])${value}(?=\\d|$|[^a-z0-9])`).test(target);
}
return new RegExp(`(^|[^a-z0-9])${value}(?=$|[^a-z0-9])`).test(target);
});
}
function buildModel(model: OpenRouterModel, existing: ExistingModel | undefined) {
const params = new Set(model.supported_parameters);
const name = model.name.replace(/^[^:]+:\s+/, "");
const input = modalities(model.architecture.input_modalities, ["text"]);
const output = modalities(model.architecture.output_modalities, ["text"]);
const prompt = price(model.pricing.prompt);
const completion = price(model.pricing.completion);
const reasoning = params.has("reasoning") || params.has("include_reasoning");
const context = model.top_provider.context_length ?? model.context_length;
const family = inferFamily(model, name);
return {
name,
family: existing?.family === "o" && family !== "o"
? family
: (existing?.family ?? family),
release_date: dateFromTimestamp(model.created),
last_updated: dateFromTimestamp(model.created),
attachment: input.some((value) => value !== "text"),
reasoning,
temperature: params.has("temperature"),
tool_call: params.has("tools") || params.has("tool_choice"),
structured_output: params.has("structured_outputs"),
knowledge: model.knowledge_cutoff?.slice(0, 10) ?? existing?.knowledge,
open_weights: Boolean(model.hugging_face_id),
status: existing?.status,
interleaved: existing?.interleaved,
cost: prompt !== undefined && completion !== undefined
? {
input: prompt,
output: completion,
reasoning: reasoning ? price(model.pricing.internal_reasoning) : undefined,
cache_read: price(model.pricing.input_cache_read),
cache_write: price(model.pricing.input_cache_write),
tiers: existing?.cost?.tiers,
}
: existing?.cost,
limit: {
context,
input: existing?.limit?.input,
output: model.top_provider.max_completion_tokens ?? existing?.limit?.output ?? context,
},
modalities: { input, output },
};
}
@@ -1,3 +1,4 @@
# See https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-moonshot-ai-kimi-k2-thinking.html
name = "Kimi K2 Thinking"
release_date = "2025-12-02"
family = "kimi-thinking"
@@ -15,8 +16,8 @@ input = 0.6
output = 2.5
[limit]
context = 256_000
output = 256_000
context = 262_143
output = 16_000
[modalities]
input = ["text"]
@@ -1,3 +1,4 @@
# See https://docs.aws.amazon.com/bedrock/latest/userguide/model-card-moonshot-ai-kimi-k2-5.html
name = "Kimi K2.5"
family = "kimi"
release_date = "2026-02-06"
@@ -15,8 +16,8 @@ input = 0.6
output = 3
[limit]
context = 256_000
output = 256_000
context = 262_143
output = 16_000
[modalities]
input = ["text", "image"]
-2
View File
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-3-mini"
-2
View File
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-3"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4-fast-non-reasoning"
-2
View File
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-code-fast-1"
@@ -0,0 +1,9 @@
release_date = "2026-05-07"
last_updated = "2026-05-07"
[extends]
from = "moonshotai/kimi-k2.6"
[cost]
input = 0.83
output = 3.85
@@ -0,0 +1,7 @@
[extends]
from = "deepseek/deepseek-v4-flash"
[cost]
input = 0.14
output = 0.28
cache_read = 0.03
@@ -0,0 +1,24 @@
name = "Agentic Chat (GPT-5.5)"
family = "gpt"
release_date = "2026-04-23"
last_updated = "2026-04-23"
knowledge = "2025-08-31"
attachment = true
reasoning = true
temperature = false
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 0
output = 0
[limit]
context = 1_050_000
input = 922_000
output = 128_000
[modalities]
input = ["text", "image", "pdf"]
output = ["text"]
@@ -1,3 +0,0 @@
[extends]
from = "google/gemini-2.5-flash-lite-preview-09-2025"
omit = ["structured_output"]
@@ -1,2 +0,0 @@
[extends]
from = "google/gemini-2.5-flash-preview-04-17"
@@ -1,3 +0,0 @@
[extends]
from = "google/gemini-2.5-flash-preview-05-20"
omit = ["structured_output"]
@@ -1,3 +0,0 @@
[extends]
from = "google/gemini-2.5-pro-preview-05-06"
omit = ["structured_output"]
@@ -1,3 +0,0 @@
[extends]
from = "google/gemini-2.5-pro-preview-06-05"
omit = ["structured_output"]
@@ -1,23 +0,0 @@
name = "Gemini 1.5 Flash-8B"
family = "gemini-flash"
release_date = "2024-10-03"
last_updated = "2024-10-03"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-04"
tool_call = true
open_weights = false
[cost]
input = 0.0375
output = 0.15
cache_read = 0.01
[limit]
context = 1_000_000
output = 8_192
[modalities]
input = ["text", "image", "audio", "video"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Gemini 1.5 Flash"
family = "gemini-flash"
release_date = "2024-05-14"
last_updated = "2024-05-14"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-04"
tool_call = true
open_weights = false
[cost]
input = 0.075
output = 0.30
cache_read = 0.01875
[limit]
context = 1_000_000
output = 8_192
[modalities]
input = ["text", "image", "audio", "video"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Gemini 1.5 Pro"
family = "gemini-pro"
release_date = "2024-02-15"
last_updated = "2024-02-15"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-04"
tool_call = true
open_weights = false
[cost]
input = 1.25
output = 5.00
cache_read = 0.3125
[limit]
context = 1_000_000
output = 8_192
[modalities]
input = ["text", "image", "audio", "video"]
output = ["text"]
@@ -1,18 +1,18 @@
name = "Gemini 2.0 Flash Lite"
name = "Gemini 2.0 Flash-Lite"
family = "gemini-flash-lite"
release_date = "2024-12-11"
last_updated = "2024-12-11"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-06"
tool_call = true
structured_output = true
knowledge = "2024-06"
open_weights = false
[cost]
input = 0.075
output = 0.30
output = 0.3
[limit]
context = 1_048_576
@@ -1,23 +0,0 @@
name = "Gemini 2.5 Flash Image (Preview)"
family = "gemini-flash"
release_date = "2025-08-26"
last_updated = "2025-08-26"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-06"
tool_call = false
open_weights = false
[cost]
input = 0.30
output = 30
cache_read = 0.075
[limit]
context = 32_768
output = 32_768
[modalities]
input = ["text", "image"]
output = ["text", "image"]
@@ -1,16 +1,16 @@
name = "Gemini 2.5 Flash Image"
name = "Nano Banana"
family = "gemini-flash"
release_date = "2025-08-26"
last_updated = "2025-08-26"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-06"
tool_call = false
knowledge = "2025-06"
open_weights = false
[cost]
input = 0.30
input = 0.3
output = 30
cache_read = 0.075
@@ -20,4 +20,4 @@ output = 32_768
[modalities]
input = ["text", "image"]
output = ["text", "image"]
output = ["text", "image"]
@@ -1,24 +0,0 @@
name = "Gemini 2.5 Flash Lite Preview 06-17"
family = "gemini-flash-lite"
release_date = "2025-06-17"
last_updated = "2025-06-17"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
open_weights = false
[cost]
input = 0.10
output = 0.40
cache_read = 0.025
input_audio = 0.30
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -1,24 +0,0 @@
name = "Gemini 2.5 Flash Lite Preview 09-25"
family = "gemini-flash-lite"
release_date = "2025-09-25"
last_updated = "2025-09-25"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 0.10
output = 0.40
cache_read = 0.025
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -1,20 +1,20 @@
name = "Gemini 2.5 Flash Lite"
name = "Gemini 2.5 Flash-Lite"
family = "gemini-flash-lite"
release_date = "2025-06-17"
last_updated = "2025-06-17"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
knowledge = "2025-01"
open_weights = false
[cost]
input = 0.10
output = 0.40
input = 0.1
output = 0.4
cache_read = 0.01
input_audio = 0.30
input_audio = 0.3
[limit]
context = 1_048_576
@@ -1,23 +0,0 @@
name = "Gemini 2.5 Flash Preview 04-17"
family = "gemini-flash"
release_date = "2025-04-17"
last_updated = "2025-04-17"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
open_weights = false
[cost]
input = 0.15
output = 0.60
cache_read = 0.0375
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -1,25 +0,0 @@
name = "Gemini 2.5 Flash Preview 09-25"
family = "gemini-flash"
release_date = "2025-09-25"
last_updated = "2025-09-25"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 0.30
output = 2.50
cache_read = 0.075
input_audio = 1.00
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -4,19 +4,19 @@ release_date = "2025-05-01"
last_updated = "2025-05-01"
attachment = false
reasoning = false
temperature = false
knowledge = "2025-01"
temperature = true
tool_call = false
knowledge = "2025-01"
open_weights = false
[cost]
input = 0.50
output = 10.00
input = 0.5
output = 10
[limit]
context = 8_000
output = 16_000
context = 8_192
output = 16_384
[modalities]
input = ["text"]
output = ["audio"]
output = ["audio"]
@@ -1,24 +0,0 @@
name = "Gemini 2.5 Pro Preview 05-06"
family = "gemini-pro"
release_date = "2025-05-06"
last_updated = "2025-05-06"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 1.25
output = 10.00
cache_read = 0.31
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -1,24 +0,0 @@
name = "Gemini 2.5 Pro Preview 06-05"
family = "gemini-pro"
release_date = "2025-06-05"
last_updated = "2025-06-05"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
open_weights = false
[cost]
input = 1.25
output = 10.00
cache_read = 0.31
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
output = ["text"]
@@ -4,19 +4,19 @@ release_date = "2025-05-01"
last_updated = "2025-05-01"
attachment = false
reasoning = false
temperature = false
knowledge = "2025-01"
temperature = true
tool_call = false
knowledge = "2025-01"
open_weights = false
[cost]
input = 1.00
output = 20.00
input = 1
output = 20
[limit]
context = 8_000
output = 16_000
context = 8_192
output = 16_384
[modalities]
input = ["text"]
output = ["audio"]
output = ["audio"]
@@ -11,19 +11,19 @@ knowledge = "2025-01"
open_weights = false
[cost]
input = 2.00
output = 12.00
cache_read = 0.20
input = 2
output = 12
cache_read = 0.2
[[cost.tiers]]
tier = { size = 200_000 }
input = 4.00
output = 18.00
cache_read = 0.40
input = 4
output = 18
cache_read = 0.4
[limit]
context = 1000000
output = 64000
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "video", "audio", "pdf"]
@@ -1,21 +1,21 @@
name = "Gemini 3.1 Flash Image (Preview)"
name = "Nano Banana 2"
family = "gemini-flash"
release_date = "2026-02-26"
last_updated = "2026-02-26"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = false
knowledge = "2025-01"
open_weights = false
[cost]
input = 0.50
output = 60.00
input = 0.5
output = 60
[limit]
context = 131_072
output = 32_768
context = 65_536
output = 65_536
[modalities]
input = ["text", "image", "pdf"]
@@ -1,24 +1,25 @@
name = "Gemini 2.5 Flash Preview 05-20"
name = "Gemini 3.5 Flash"
family = "gemini-flash"
release_date = "2025-05-20"
last_updated = "2025-05-20"
release_date = "2026-05-19"
last_updated = "2026-05-19"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
structured_output = true
knowledge = "2025-01"
open_weights = false
[cost]
input = 0.15
output = 0.60
cache_read = 0.0375
input = 1.50
output = 9.00
cache_read = 0.15
input_audio = 1.50
[limit]
context = 1_048_576
output = 65_536
[modalities]
input = ["text", "image", "audio", "video", "pdf"]
input = ["text", "image", "video", "audio", "pdf"]
output = ["text"]
@@ -5,17 +5,17 @@ last_updated = "2025-05-20"
attachment = false
reasoning = false
temperature = false
knowledge = "2025-05"
tool_call = false
knowledge = "2025-05"
open_weights = false
[cost]
input = 0.15
output = 0.00
output = 0
[limit]
context = 2_048
output = 3_072
output = 1
[modalities]
input = ["text"]
@@ -1,24 +0,0 @@
name = "Gemini Live 2.5 Flash Preview Native Audio"
family = "gemini-flash"
release_date = "2025-06-17"
last_updated = "2025-09-18"
attachment = false
reasoning = true
temperature = false
knowledge = "2025-01"
tool_call = true
open_weights = false
[cost]
input = 0.50
output = 2.00
input_audio = 3.00
output_audio = 12.00
[limit]
context = 131_072
output = 65_536
[modalities]
input = ["text", "audio", "video"]
output = ["text", "audio"]
@@ -1,24 +0,0 @@
name = "Gemini Live 2.5 Flash"
family = "gemini-flash"
release_date = "2025-09-01"
last_updated = "2025-09-01"
attachment = true
reasoning = true
temperature = true
knowledge = "2025-01"
tool_call = true
open_weights = false
[cost]
input = 0.50
output = 2.00
input_audio = 3.00
output_audio = 12.00
[limit]
context = 128_000
output = 8_000
[modalities]
input = ["text", "image", "audio", "video"]
output = ["text", "audio"]
@@ -1,23 +0,0 @@
name = "Gemma 3 12B"
family = "gemma"
release_date = "2025-03-13"
last_updated = "2025-03-13"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-10"
tool_call = false
structured_output = true
open_weights = true
[cost]
input = 0
output = 0
[limit]
context = 32_768
output = 8_192
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,23 +0,0 @@
name = "Gemma 3 27B"
family = "gemma"
release_date = "2025-03-12"
last_updated = "2025-03-12"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-10"
tool_call = true
structured_output = true
open_weights = true
[cost]
input = 0
output = 0
[limit]
context = 131_072
output = 8_192
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,22 +0,0 @@
name = "Gemma 3 4B"
family = "gemma"
release_date = "2025-03-13"
last_updated = "2025-03-13"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-10"
tool_call = false
open_weights = true
[cost]
input = 0
output = 0
[limit]
context = 32_768
output = 8_192
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,22 +0,0 @@
name = "Gemma 3n 2B"
family = "gemma"
release_date = "2025-07-09"
last_updated = "2025-07-09"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-10"
tool_call = false
open_weights = true
[cost]
input = 0
output = 0
[limit]
context = 8_192
output = 2_000
[modalities]
input = ["text"]
output = ["text"]
@@ -1,22 +0,0 @@
name = "Gemma 3n 4B"
family = "gemma"
release_date = "2025-05-20"
last_updated = "2025-05-20"
attachment = true
reasoning = false
temperature = true
knowledge = "2024-10"
tool_call = false
open_weights = true
[cost]
input = 0
output = 0
[limit]
context = 8_192
output = 2_000
[modalities]
input = ["text"]
output = ["text"]
@@ -1,8 +1,8 @@
name = "Gemma 4 26B"
name = "Gemma 4 26B A4B IT"
family = "gemma"
release_date = "2026-04-02"
last_updated = "2026-04-02"
attachment = false
attachment = true
reasoning = true
temperature = true
tool_call = true
@@ -10,8 +10,8 @@ structured_output = true
open_weights = true
[limit]
context = 256000
output = 8192
context = 262_144
output = 32_768
[modalities]
input = ["text", "image"]
+4 -4
View File
@@ -1,8 +1,8 @@
name = "Gemma 4 31B"
name = "Gemma 4 31B IT"
family = "gemma"
release_date = "2026-04-02"
last_updated = "2026-04-02"
attachment = false
attachment = true
reasoning = true
temperature = true
tool_call = true
@@ -10,8 +10,8 @@ structured_output = true
open_weights = true
[limit]
context = 256000
output = 8192
context = 262_144
output = 32_768
[modalities]
input = ["text", "image"]
+1 -1
View File
@@ -1,4 +1,4 @@
name = "Google"
env = ["GOOGLE_GENERATIVE_AI_API_KEY", "GEMINI_API_KEY"]
env = ["GOOGLE_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY", "GEMINI_API_KEY"]
npm = "@ai-sdk/google"
doc = "https://ai.google.dev/gemini-api/docs/models"
@@ -1,20 +0,0 @@
name = "Goliath 120B"
release_date = "2023-11-10"
last_updated = "2026-03-15"
attachment = false
reasoning = false
temperature = true
tool_call = false
open_weights = true
[cost]
input = 3.75
output = 7.5
[limit]
context = 6144
output = 1024
[modalities]
input = ["text"]
output = ["text"]
@@ -1,22 +0,0 @@
name = "Anthropic: Claude 3.7 Sonnet"
release_date = "2025-02-19"
last_updated = "2026-03-15"
attachment = true
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 3
output = 15
cache_read = 0.3
cache_write = 3.75
[limit]
context = 200000
output = 64000
[modalities]
input = ["image", "pdf", "text"]
output = ["text"]
@@ -1,22 +0,0 @@
name = "Anthropic: Claude 3.7 Sonnet (thinking)"
release_date = "2025-02-19"
last_updated = "2026-03-15"
attachment = true
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 3
output = 15
cache_read = 0.3
cache_write = 3.75
[limit]
context = 200000
output = 64000
[modalities]
input = ["image", "pdf", "text"]
output = ["text"]
@@ -0,0 +1,22 @@
name = "Anthropic: Claude Opus 4.7 (Fast)"
release_date = "2026-05-12"
last_updated = "2026-05-16"
attachment = true
reasoning = true
tool_call = true
temperature = false
open_weights = false
[cost]
input = 30.0
output = 150.0
cache_read = 3.0
cache_write = 37.5
[limit]
context = 1000000
output = 128000
[modalities]
input = ["image", "pdf", "text"]
output = ["text"]
@@ -1,6 +1,6 @@
name = "Baidu: Qianfan-OCR-Fast (free)"
name = "Baidu: Qianfan-OCR-Fast"
release_date = "2026-04-20"
last_updated = "2026-05-01"
last_updated = "2026-05-16"
attachment = true
reasoning = true
tool_call = false
@@ -8,8 +8,8 @@ temperature = true
open_weights = false
[cost]
input = 0.0
output = 0.0
input = 0.68
output = 2.81
[limit]
context = 65536
@@ -0,0 +1,20 @@
name = "DeepSeek: DeepSeek V4 Flash (free)"
release_date = "2026-04-24"
last_updated = "2026-05-16"
attachment = false
reasoning = true
tool_call = true
temperature = false
open_weights = false
[cost]
input = 0
output = 0
[limit]
context = 1048576
output = 384000
[modalities]
input = ["text"]
output = ["text"]
@@ -0,0 +1,23 @@
name = "Google: Gemini 3.1 Flash Lite"
release_date = "2026-05-07"
last_updated = "2026-05-16"
attachment = true
reasoning = true
tool_call = true
temperature = true
open_weights = false
[cost]
input = 0.25
output = 1.5
reasoning = 1.5
cache_read = 0.025
cache_write = 0.08333
[limit]
context = 1048576
output = 65536
[modalities]
input = ["audio", "image", "pdf", "text", "video"]
output = ["text"]
@@ -1,6 +1,6 @@
name = "inclusionAI: Ling-2.6-1T (free)"
name = "inclusionAI: Ling-2.6-1T"
release_date = "2026-04-23"
last_updated = "2026-05-01"
last_updated = "2026-05-16"
attachment = false
reasoning = false
tool_call = true
@@ -8,8 +8,9 @@ temperature = true
open_weights = false
[cost]
input = 0.0
output = 0.0
input = 0.3
output = 2.5
cache_read = 0.06
[limit]
context = 262144
@@ -0,0 +1,21 @@
name = "inclusionAI: Ring-2.6-1T"
release_date = "2026-05-08"
last_updated = "2026-05-16"
attachment = false
reasoning = true
tool_call = true
temperature = true
open_weights = false
[cost]
input = 0.075
output = 0.625
cache_read = 0.015
[limit]
context = 262144
output = 65536
[modalities]
input = ["text"]
output = ["text"]
@@ -1,20 +0,0 @@
name = "Mistral: Mixtral 8x7B Instruct"
release_date = "2023-12-10"
last_updated = "2026-03-15"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = true
[cost]
input = 0.54
output = 0.54
[limit]
context = 32768
output = 16384
[modalities]
input = ["text"]
output = ["text"]
@@ -1,20 +0,0 @@
name = "NVIDIA: Llama 3.1 Nemotron 70B Instruct"
release_date = "2024-10-12"
last_updated = "2024-10-12"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 1.2
output = 1.2
[limit]
context = 131072
output = 16384
[modalities]
input = ["text"]
output = ["text"]
@@ -0,0 +1,20 @@
name = "Perceptron: Perceptron Mk1"
release_date = "2026-05-12"
last_updated = "2026-05-16"
attachment = true
reasoning = true
tool_call = false
temperature = true
open_weights = false
[cost]
input = 0.15
output = 1.5
[limit]
context = 32768
output = 8192
[modalities]
input = ["image", "text", "video"]
output = ["text"]
-21
View File
@@ -1,21 +0,0 @@
name = "Qwen: Qwen-Max "
release_date = "2024-04-03"
last_updated = "2026-03-15"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 1.04
output = 4.16
cache_read = 0.32
[limit]
context = 32768
output = 8192
[modalities]
input = ["text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "Qwen: Qwen-Turbo"
release_date = "2024-11-01"
last_updated = "2026-03-15"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.0325
output = 0.13
cache_read = 0.01
[limit]
context = 131072
output = 8192
[modalities]
input = ["text"]
output = ["text"]
@@ -1,20 +0,0 @@
name = "Qwen: Qwen VL Max"
release_date = "2024-04-08"
last_updated = "2025-08-13"
attachment = true
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.8
output = 3.2
[limit]
context = 131072
output = 32768
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "Qwen: Qwen VL Plus"
release_date = "2024-01-25"
last_updated = "2026-03-15"
attachment = true
reasoning = false
temperature = true
tool_call = false
open_weights = false
[cost]
input = 0.1365
output = 0.4095
cache_read = 0.042
[limit]
context = 131072
output = 8192
[modalities]
input = ["image", "text"]
output = ["text"]
@@ -1,6 +1,6 @@
name = "Tencent: Hy3 Preview (free)"
name = "Tencent: Hy3 Preview"
release_date = "2026-04-22"
last_updated = "2026-05-01"
last_updated = "2026-05-16"
attachment = false
reasoning = true
tool_call = true
@@ -8,8 +8,9 @@ temperature = true
open_weights = false
[cost]
input = 0.0
output = 0.0
input = 0.066
output = 0.26
cache_read = 0.029
[limit]
context = 262144
@@ -1,21 +0,0 @@
name = "TNG: DeepSeek R1T2 Chimera"
release_date = "2025-07-08"
last_updated = "2025-07-08"
attachment = false
reasoning = true
temperature = true
tool_call = true
open_weights = true
[cost]
input = 0.25
output = 0.85
cache_read = 0.125
[limit]
context = 163840
output = 163840
[modalities]
input = ["text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok 3 Beta"
release_date = "2025-02-17"
last_updated = "2025-02-17"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 3.0
output = 15.0
cache_read = 0.75
[limit]
context = 131072
output = 26215
[modalities]
input = ["text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok 3 Mini Beta"
release_date = "2025-02-17"
last_updated = "2025-02-17"
attachment = false
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.3
output = 0.5
cache_read = 0.075
[limit]
context = 131072
output = 26215
[modalities]
input = ["text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok 3 Mini"
release_date = "2025-02-17"
last_updated = "2025-02-17"
attachment = false
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.3
output = 0.5
cache_read = 0.075
[limit]
context = 131072
output = 26215
[modalities]
input = ["text"]
output = ["text"]
-21
View File
@@ -1,21 +0,0 @@
name = "xAI: Grok 3"
release_date = "2025-02-17"
last_updated = "2025-02-17"
attachment = false
reasoning = false
temperature = true
tool_call = true
open_weights = false
[cost]
input = 3.0
output = 15.0
cache_read = 0.75
[limit]
context = 131072
output = 26215
[modalities]
input = ["text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok 4 Fast"
release_date = "2025-08-19"
last_updated = "2025-08-19"
attachment = true
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.2
output = 0.5
cache_read = 0.05
[limit]
context = 2000000
output = 30000
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok 4.1 Fast"
release_date = "2025-11-19"
last_updated = "2025-11-19"
attachment = true
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.2
output = 0.5
cache_read = 0.05
[limit]
context = 2000000
output = 30000
[modalities]
input = ["text", "image"]
output = ["text"]
-21
View File
@@ -1,21 +0,0 @@
name = "xAI: Grok 4"
release_date = "2025-07-09"
last_updated = "2025-07-09"
attachment = true
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 3.0
output = 15.0
cache_read = 0.75
[limit]
context = 256000
output = 51200
[modalities]
input = ["image", "text"]
output = ["text"]
@@ -1,21 +0,0 @@
name = "xAI: Grok Code Fast 1"
release_date = "2025-08-26"
last_updated = "2025-08-26"
attachment = false
reasoning = true
temperature = true
tool_call = true
open_weights = false
[cost]
input = 0.2
output = 1.5
cache_read = 0.02
[limit]
context = 256000
output = 10000
[modalities]
input = ["text"]
output = ["text"]
+6
View File
@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 510 510" fill="currentColor">
<path d="M316.18,14.51l-20.5,34.9c-8.66,14.74-24.47,23.79-41.56,23.79h0c-17.09,0-32.91-9.05-41.56-23.79l-20.5-34.9c-15.98-27.2-53.34-31.98-75.65-9.67l-7.48,7.48c-18.82,18.82-18.82,49.35,0,68.17l81.74,81.74c9.04,9.04,21.3,14.12,34.09,14.12h58.73c12.78,0,25.05-5.08,34.09-14.12l81.74-81.74c18.82-18.82,18.82-49.35,0-68.17l-7.48-7.48c-22.31-22.31-59.67-17.53-75.65,9.67Z"/>
<path d="M192.06,495.81l20.5-34.9c8.66-14.74,24.47-23.79,41.56-23.79h0c17.09,0,32.91,9.05,41.56,23.79l20.5,34.9c15.98,27.2,53.34,31.98,75.65,9.67l7.48-7.48c18.82-18.82,18.82-49.35,0-68.17l-81.74-81.74c-9.04-9.04-21.3-14.12-34.09-14.12h-58.73c-12.78,0-25.05,5.08-34.09,14.12l-81.74,81.74c-18.82,18.82-18.82,49.35,0,68.17l7.48,7.48c22.31,22.31,59.67,17.53,75.65-9.67Z"/>
<path d="M494.77,317.23l-34.9-20.5c-14.74-8.66-23.79-24.47-23.79-41.56h0c0-17.09,9.05-32.91,23.79-41.56l34.9-20.5c27.2-15.98,31.98-53.34,9.67-75.65l-7.48-7.48c-18.82-18.82-49.35-18.82-68.17,0l-81.74,81.74c-9.04,9.04-14.12,21.3-14.12,34.09v58.73c0,12.78,5.08,25.05,14.12,34.09l81.74,81.74c18.82,18.82,49.35,18.82,68.17,0l7.48-7.48c22.31-22.31,17.53-59.67-9.67-75.65Z"/>
<path d="M13.47,193.1l34.9,20.5c14.74,8.66,23.79,24.47,23.79,41.56h0c0,17.09-9.05,32.91-23.79,41.56l-34.9,20.5c-27.2,15.98-31.98,53.34-9.67,75.65l7.48,7.48c18.82,18.82,49.35,18.82,68.17,0l81.74-81.74c9.04-9.04,14.12-21.3,14.12-34.09v-58.73c0-12.78-5.08-25.05-14.12-34.09l-81.74-81.74c-18.82-18.82-49.35-18.82-68.17,0l-7.48,7.48c-22.31,22.31-17.53,59.67,9.67,75.65Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,21 @@
name = "Gemma 4 31B IT"
attachment = true
knowledge = "2025-01"
[extends]
from = "google/gemma-4-31b-it"
[interleaved]
field = "reasoning_content"
[cost]
input = 0.11
output = 0.35
[limit]
context = 262_100
output = 262_100
[modalities]
input = ["text", "image", "video"]
output = ["text"]
@@ -0,0 +1,23 @@
name = "MiniMax M2.7"
structured_output = true
knowledge = "2025-01"
[extends]
from = "minimax/MiniMax-M2.7"
omit = ["cost.cache_write"]
[interleaved]
field = "reasoning_content"
[cost]
input = 0.30
output = 1.20
cache_read = 0.055
[limit]
context = 204_800
output = 204_800
[modalities]
input = ["text"]
output = ["text"]
@@ -0,0 +1,11 @@
[extends]
from = "moonshotai/kimi-k2.6"
[cost]
input = 0.70
output = 3.50
cache_read = 0.20
[modalities]
input = ["text", "image"]
output = ["text"]
@@ -0,0 +1,20 @@
name = "GLM 5.1"
knowledge = "2025-04"
open_weights = true
[extends]
from = "zai/glm-5.1"
omit = ["cost.cache_write"]
[cost]
input = 0.90
output = 3.00
cache_read = 0.27
[limit]
context = 202_800
output = 131_072
[modalities]
input = ["text"]
output = ["text"]
+5
View File
@@ -0,0 +1,5 @@
name = "Lilac"
env = ["LILAC_API_KEY"]
npm = "@ai-sdk/openai-compatible"
api = "https://api.getlilac.com/v1"
doc = "https://docs.getlilac.com/inference/models"
@@ -1,2 +0,0 @@
[extends]
from = "google/gemini-2.5-flash-lite-preview-09-2025"
@@ -1,2 +0,0 @@
[extends]
from = "google/gemma-3-12b-it"
@@ -1,2 +0,0 @@
[extends]
from = "google/gemma-3-4b-it"
@@ -1,2 +0,0 @@
[extends]
from = "google/gemma-3n-e2b-it"
@@ -1,2 +0,0 @@
[extends]
from = "google/gemma-3n-e4b-it"
-2
View File
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-3"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4-1-fast-non-reasoning"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4-1-fast"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4-fast-non-reasoning"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4-fast"
-2
View File
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-4"
@@ -1,2 +0,0 @@
[extends]
from = "xai/grok-code-fast-1"
+3 -3
View File
@@ -14,7 +14,6 @@ Reasoning Models (with interleaved thinking):
- zai-org/GLM-5.1-FP8 — GLM 5.1 FP8, reasoning enabled
- moonshotai/Kimi-K2.5 — Kimi K2.5, reasoning + image input
- moonshotai/Kimi-K2.6 — Kimi K2.6, reasoning + image input
- kimi-k2.6-fast — Kimi K2.6 Fast, reasoning + image input
- MiniMaxAI/MiniMax-M2.5 — MiniMax M2.5, reasoning enabled
- Qwen/Qwen3.5-397B-A17B-FP8 — Qwen3.5 397B, reasoning enabled
- Qwen/Qwen3.6-35B-A3B — Qwen3.6 35B A3B, reasoning enabled
@@ -24,6 +23,7 @@ Fast Variants (optimized for speed, non-reasoning):
- glm-5-fast — GLM 5 Fast
- glm-5.1-fast — GLM 5.1 Fast
- kimi-k2.5-fast — Kimi K2.5 Fast, image input
- kimi-k2.6-fast — Kimi K2.6 Fast, image input
- qwen3.5-397b-fast — Qwen3.5 397B Fast
- qwen3.6-35b-fast — Qwen3.6 35B Fast
@@ -31,7 +31,7 @@ Other:
- mistralai/Devstral-Small-2-24B-Instruct-2512 — Devstral Small 2, code-focused + image input
Notes
- Model IDs and pricing sourced directly from the Neuralwatt API (now fully accurate)
- Model IDs, pricing, and limits sourced directly from the Neuralwatt API
- Neuralwatt provides real-time energy consumption data (Joules/kWh) per request
- "Fast" variants are optimized for lower latency; some fast variants also support reasoning (kimi-k2.6-fast)
- "Fast" variants are optimized for lower latency without reasoning
- Vision models support image input via OpenAI-compatible API
@@ -13,8 +13,8 @@ input = 0.35
output = 1.38
[limit]
context = 196_608
output = 196_608
context = 196_592
output = 196_592
[modalities]
input = ["text"]
@@ -13,8 +13,8 @@ input = 0.69
output = 4.14
[limit]
context = 262_144
output = 262_144
context = 262_128
output = 262_128
[modalities]
input = ["text"]
@@ -13,8 +13,8 @@ input = 0.05
output = 0.10
[limit]
context = 131_072
output = 131_072
context = 131_056
output = 131_056
[modalities]
input = ["text", "image"]
+2 -2
View File
@@ -13,8 +13,8 @@ input = 1.1
output = 3.6
[limit]
context = 200_000
output = 200_000
context = 202_736
output = 202_736
[modalities]
input = ["text"]

Some files were not shown because too many files have changed in this diff Show More