diff --git a/packages/llm-info/README.md b/packages/llm-info/README.md index 40b57532295..46913b8dc3f 100644 --- a/packages/llm-info/README.md +++ b/packages/llm-info/README.md @@ -15,7 +15,7 @@ If you want to add a new LLM model or provider, you can do so by editing the YAM ## Syncing from `models.dev` -`pnpm sync-models` appends latest models from [`models.dev`](https://models.dev/api.json) to `models.yml`. Existing entries are preserved. Run `pnpm codegen` afterwards. +`pnpm sync-models` adds the latest models from [`models.dev`](https://models.dev/api.json) to the top of each provider section in `models.yml`. Existing entries are preserved. Run `pnpm codegen` afterwards. ```bash pnpm sync-models # all providers, 10 newest each diff --git a/packages/llm-info/data/models.yml b/packages/llm-info/data/models.yml index f01d97c9f2d..e7ce927a857 100644 --- a/packages/llm-info/data/models.yml +++ b/packages/llm-info/data/models.yml @@ -1,4 +1,15 @@ anthropic: + + - name: Claude Opus 4.8 + model: claude-opus-4-8 + description: "Modest but tangible improvement over Claude Opus 4.7" + roles: [chat, edit] + capabilities: [thinking, tool_calling] + input_types: [text, image, pdf] + output_types: [text] + release_date: 2026-05-28 + cost: {input: 5, output: 25} + - name: Claude Opus 4.7 model: claude-opus-4-7 description: "Next generation of Anthropic's Opus family, built for long-running, asynchronous agents" @@ -199,6 +210,17 @@ google: cost: {input: 2, output: 12} bedrock: + + - name: Claude Opus 4.8 (Global) + model: global.anthropic.claude-opus-4-8 + description: "Modest but tangible improvement over Claude Opus 4.7" + roles: [chat, edit] + capabilities: [thinking, tool_calling] + input_types: [text, image, pdf] + output_types: [text] + release_date: 2026-05-28 + cost: {input: 5, output: 25} + - name: Claude Opus 4.7 model: anthropic.claude-opus-4-7 description: "Next generation of Anthropic's Opus family, built for long-running, asynchronous agents" @@ -654,6 +676,17 @@ wandb: cost: {input: 0.1, output: 0.1} opencode-go: + + - name: Qwen3.7 Max + model: qwen3.7-max + description: "Capable of writing and debugging code, and sustaining autonomous execution across hundreds or thousands of steps" + roles: [chat, edit] + capabilities: [thinking, tool_calling] + input_types: [text] + output_types: [text] + release_date: 2026-05-21 + cost: {input: 2.5, output: 7.5} + - name: DeepSeek V4 Flash model: deepseek-v4-flash description: "Efficiency-optimized Mixture-of-Experts model from DeepSeek with 284B total parameters and 13B activated parameters, supporting a 1M-token context window" diff --git a/packages/llm-info/src/__tests__/sync-models.test.ts b/packages/llm-info/src/__tests__/sync-models.test.ts index b9d08d1cc31..46df242e1e4 100644 --- a/packages/llm-info/src/__tests__/sync-models.test.ts +++ b/packages/llm-info/src/__tests__/sync-models.test.ts @@ -635,7 +635,7 @@ describe("syncModels", () => { writeFileSync(yamlPath, FIXTURE_YAML); }); - it("appends new entries to existing provider sections and creates new ones", async () => { + it("prepends new entries to existing provider sections and creates new ones", async () => { const result = await syncModels({ modelsYamlPath: yamlPath, modelsDev: FIXTURE_API, @@ -664,6 +664,10 @@ describe("syncModels", () => { ); expect(opus45.description).toBe("A hand-written description."); expect(opus45.name).toBe("Claude Opus 4.5"); + expect(parsed.anthropic.map((m: { model: string }) => m.model)).toEqual([ + "claude-opus-4-7", + "claude-opus-4-5", + ]); }); it("renders flow-style arrays and a blank line between entries", async () => { diff --git a/packages/llm-info/src/sync-models.ts b/packages/llm-info/src/sync-models.ts index 5855c8114d4..d5d29030c52 100644 --- a/packages/llm-info/src/sync-models.ts +++ b/packages/llm-info/src/sync-models.ts @@ -39,7 +39,7 @@ interface SyncOptions { modelsDev?: ModelsDevApi; write?: boolean; mode?: SyncMode; - /** Cap on new entries appended per provider section. */ + /** Cap on new entries inserted per provider section. */ maxPerProvider?: number; /** Restrict sync to these marimo provider ids (defaults to all). */ providers?: readonly string[]; @@ -170,10 +170,10 @@ function renderFresh(entries: ModelsByProvider): string { } /** - * Append new entries into an existing document, creating provider sections if + * Insert new entries into an existing document, creating provider sections if * needed. Preserves comments and ordering of unchanged sections. */ -function appendIntoDocument( +function insertIntoDocument( yamlText: string, newEntries: ModelsByProvider, ): string { @@ -199,13 +199,20 @@ function appendIntoDocument( // New section appended after existing content — always blank-line separated. addProviderSection(doc, root, provider, seq, true); } - for (const model of models) { + const originalFirstItem = seq.items[0] as { spaceBefore?: boolean }; + const newItems: YAMLMap[] = []; + for (const [index, model] of models.entries()) { const item = buildEntryNode(doc, model); - if (seq.items.length > 0) { + if (index > 0 || originalFirstItem?.spaceBefore) { (item as { spaceBefore?: boolean }).spaceBefore = true; } - seq.items.push(item); + newItems.push(item); + } + if (newItems.length > 0 && seq.items.length > 0) { + // Preserve a blank line between the prepended block and curated entries. + originalFirstItem.spaceBefore = true; } + seq.items.unshift(...newItems); } return doc.toString({ lineWidth: 0, flowCollectionPadding: false }); @@ -253,7 +260,7 @@ export async function syncModels(options: SyncOptions): Promise { const yaml = isFresh ? renderFresh(summary.newEntries) : addedCount > 0 - ? appendIntoDocument(existingText, summary.newEntries) + ? insertIntoDocument(existingText, summary.newEntries) : existingText; if (write && (addedCount > 0 || mode === "replace")) {